Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / supervised_user / supervised_user_settings_service.cc
bloba2be4f720e2346ba39abd29315b15ae5ba4d684d
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/supervised_user/supervised_user_settings_service.h"
7 #include "base/callback.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/json_writer.h"
10 #include "base/prefs/json_pref_store.h"
11 #include "base/prefs/pref_filter.h"
12 #include "base/strings/string_util.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "chrome/browser/supervised_user/supervised_user_url_filter.h"
15 #include "chrome/common/chrome_constants.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/user_metrics.h"
18 #include "sync/api/sync_change.h"
19 #include "sync/api/sync_error_factory.h"
20 #include "sync/protocol/sync.pb.h"
22 using base::DictionaryValue;
23 using base::JSONReader;
24 using base::UserMetricsAction;
25 using base::Value;
26 using content::BrowserThread;
27 using syncer::SUPERVISED_USER_SETTINGS;
28 using syncer::ModelType;
29 using syncer::SyncChange;
30 using syncer::SyncChangeList;
31 using syncer::SyncChangeProcessor;
32 using syncer::SyncData;
33 using syncer::SyncDataList;
34 using syncer::SyncError;
35 using syncer::SyncErrorFactory;
36 using syncer::SyncMergeResult;
38 const char kAtomicSettings[] = "atomic_settings";
39 const char kSupervisedUserInternalItemPrefix[] = "X-";
40 const char kQueuedItems[] = "queued_items";
41 const char kSplitSettingKeySeparator = ':';
42 const char kSplitSettings[] = "split_settings";
44 namespace {
46 bool SettingShouldApplyToPrefs(const std::string& name) {
47 return !base::StartsWith(name, kSupervisedUserInternalItemPrefix,
48 base::CompareCase::INSENSITIVE_ASCII);
51 } // namespace
53 SupervisedUserSettingsService::SupervisedUserSettingsService(Profile* profile)
54 : profile_(profile),
55 active_(false),
56 initialization_failed_(false),
57 local_settings_(new base::DictionaryValue) {
60 SupervisedUserSettingsService::~SupervisedUserSettingsService() {}
62 void SupervisedUserSettingsService::Init(
63 base::FilePath profile_path,
64 base::SequencedTaskRunner* sequenced_task_runner,
65 bool load_synchronously) {
66 base::FilePath path =
67 profile_path.Append(chrome::kSupervisedUserSettingsFilename);
68 PersistentPrefStore* store = new JsonPrefStore(
69 path, sequenced_task_runner, scoped_ptr<PrefFilter>());
70 Init(store);
71 if (load_synchronously) {
72 store_->ReadPrefs();
73 // TODO(bauerb): Temporary CHECK while investigating
74 // https://crbug.com/425785. Remove (or change to DCHECK) once the bug
75 // is fixed.
76 CHECK(store_->IsInitializationComplete());
77 } else {
78 store_->ReadPrefsAsync(NULL);
82 void SupervisedUserSettingsService::Init(
83 scoped_refptr<PersistentPrefStore> store) {
84 DCHECK(!store_.get());
85 store_ = store;
86 store_->AddObserver(this);
89 scoped_ptr<SupervisedUserSettingsService::SettingsCallbackList::Subscription>
90 SupervisedUserSettingsService::Subscribe(
91 const SettingsCallback& callback) {
92 if (IsReady()) {
93 scoped_ptr<base::DictionaryValue> settings = GetSettings();
94 callback.Run(settings.get());
97 return callback_list_.Add(callback);
100 Profile* SupervisedUserSettingsService::GetProfile(){
101 return profile_;
104 void SupervisedUserSettingsService::SetActive(bool active) {
105 active_ = active;
106 InformSubscribers();
109 bool SupervisedUserSettingsService::IsReady() {
110 // Initialization cannot be complete but have failed at the same time.
111 DCHECK(!(store_->IsInitializationComplete() && initialization_failed_));
112 return initialization_failed_ || store_->IsInitializationComplete();
115 void SupervisedUserSettingsService::Clear() {
116 store_->RemoveValue(kAtomicSettings,
117 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
118 store_->RemoveValue(kSplitSettings,
119 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
122 // static
123 std::string SupervisedUserSettingsService::MakeSplitSettingKey(
124 const std::string& prefix,
125 const std::string& key) {
126 return prefix + kSplitSettingKeySeparator + key;
129 void SupervisedUserSettingsService::UploadItem(const std::string& key,
130 scoped_ptr<base::Value> value) {
131 DCHECK(!SettingShouldApplyToPrefs(key));
133 std::string key_suffix = key;
134 base::DictionaryValue* dict = NULL;
135 if (sync_processor_) {
136 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Syncing"));
137 dict = GetDictionaryAndSplitKey(&key_suffix);
138 DCHECK(GetQueuedItems()->empty());
139 SyncChangeList change_list;
140 SyncData data = CreateSyncDataForSetting(key, *value);
141 SyncChange::SyncChangeType change_type =
142 dict->HasKey(key_suffix) ? SyncChange::ACTION_UPDATE
143 : SyncChange::ACTION_ADD;
144 change_list.push_back(SyncChange(FROM_HERE, change_type, data));
145 SyncError error =
146 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
147 DCHECK(!error.IsSet()) << error.ToString();
148 } else {
149 // Queue the item up to be uploaded when we start syncing
150 // (in MergeDataAndStartSyncing()).
151 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Queued"));
152 dict = GetQueuedItems();
154 dict->SetWithoutPathExpansion(key_suffix, value.release());
157 void SupervisedUserSettingsService::SetLocalSetting(
158 const std::string& key,
159 scoped_ptr<base::Value> value) {
160 if (value)
161 local_settings_->SetWithoutPathExpansion(key, value.release());
162 else
163 local_settings_->RemoveWithoutPathExpansion(key, NULL);
165 InformSubscribers();
168 // static
169 SyncData SupervisedUserSettingsService::CreateSyncDataForSetting(
170 const std::string& name,
171 const base::Value& value) {
172 std::string json_value;
173 base::JSONWriter::Write(value, &json_value);
174 ::sync_pb::EntitySpecifics specifics;
175 specifics.mutable_managed_user_setting()->set_name(name);
176 specifics.mutable_managed_user_setting()->set_value(json_value);
177 return SyncData::CreateLocalData(name, name, specifics);
180 void SupervisedUserSettingsService::Shutdown() {
181 store_->RemoveObserver(this);
184 SyncMergeResult SupervisedUserSettingsService::MergeDataAndStartSyncing(
185 ModelType type,
186 const SyncDataList& initial_sync_data,
187 scoped_ptr<SyncChangeProcessor> sync_processor,
188 scoped_ptr<SyncErrorFactory> error_handler) {
189 DCHECK_EQ(SUPERVISED_USER_SETTINGS, type);
190 sync_processor_ = sync_processor.Pass();
191 error_handler_ = error_handler.Pass();
193 // Clear all atomic and split settings, then recreate them from Sync data.
194 Clear();
195 for (const SyncData& sync_data : initial_sync_data) {
196 DCHECK_EQ(SUPERVISED_USER_SETTINGS, sync_data.GetDataType());
197 const ::sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
198 sync_data.GetSpecifics().managed_user_setting();
199 scoped_ptr<base::Value> value(
200 JSONReader::DeprecatedRead(supervised_user_setting.value()));
201 std::string name_suffix = supervised_user_setting.name();
202 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&name_suffix);
203 dict->SetWithoutPathExpansion(name_suffix, value.release());
205 store_->ReportValueChanged(kAtomicSettings,
206 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
207 store_->ReportValueChanged(kSplitSettings,
208 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
209 InformSubscribers();
211 // Upload all the queued up items (either with an ADD or an UPDATE action,
212 // depending on whether they already exist) and move them to split settings.
213 SyncChangeList change_list;
214 base::DictionaryValue* queued_items = GetQueuedItems();
215 for (base::DictionaryValue::Iterator it(*queued_items); !it.IsAtEnd();
216 it.Advance()) {
217 std::string key_suffix = it.key();
218 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&key_suffix);
219 SyncData data = CreateSyncDataForSetting(it.key(), it.value());
220 SyncChange::SyncChangeType change_type =
221 dict->HasKey(key_suffix) ? SyncChange::ACTION_UPDATE
222 : SyncChange::ACTION_ADD;
223 change_list.push_back(SyncChange(FROM_HERE, change_type, data));
224 dict->SetWithoutPathExpansion(key_suffix, it.value().DeepCopy());
226 queued_items->Clear();
228 SyncMergeResult result(SUPERVISED_USER_SETTINGS);
229 // Process all the accumulated changes from the queued items.
230 if (change_list.size() > 0) {
231 store_->ReportValueChanged(kQueuedItems,
232 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
233 result.set_error(
234 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list));
237 // TODO(bauerb): Statistics?
238 return result;
241 void SupervisedUserSettingsService::StopSyncing(ModelType type) {
242 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS, type);
243 sync_processor_.reset();
244 error_handler_.reset();
247 SyncDataList SupervisedUserSettingsService::GetAllSyncData(
248 ModelType type) const {
249 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS, type);
250 SyncDataList data;
251 for (base::DictionaryValue::Iterator it(*GetAtomicSettings()); !it.IsAtEnd();
252 it.Advance()) {
253 data.push_back(CreateSyncDataForSetting(it.key(), it.value()));
255 for (base::DictionaryValue::Iterator it(*GetSplitSettings()); !it.IsAtEnd();
256 it.Advance()) {
257 const base::DictionaryValue* dict = NULL;
258 it.value().GetAsDictionary(&dict);
259 for (base::DictionaryValue::Iterator jt(*dict);
260 !jt.IsAtEnd(); jt.Advance()) {
261 data.push_back(CreateSyncDataForSetting(
262 MakeSplitSettingKey(it.key(), jt.key()), jt.value()));
265 DCHECK_EQ(0u, GetQueuedItems()->size());
266 return data;
269 SyncError SupervisedUserSettingsService::ProcessSyncChanges(
270 const tracked_objects::Location& from_here,
271 const SyncChangeList& change_list) {
272 for (const SyncChange& sync_change : change_list) {
273 SyncData data = sync_change.sync_data();
274 DCHECK_EQ(SUPERVISED_USER_SETTINGS, data.GetDataType());
275 const ::sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
276 data.GetSpecifics().managed_user_setting();
277 std::string key = supervised_user_setting.name();
278 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&key);
279 SyncChange::SyncChangeType change_type = sync_change.change_type();
280 switch (change_type) {
281 case SyncChange::ACTION_ADD:
282 case SyncChange::ACTION_UPDATE: {
283 scoped_ptr<base::Value> value(
284 JSONReader::DeprecatedRead(supervised_user_setting.value()));
285 if (dict->HasKey(key)) {
286 DLOG_IF(WARNING, change_type == SyncChange::ACTION_ADD)
287 << "Value for key " << key << " already exists";
288 } else {
289 DLOG_IF(WARNING, change_type == SyncChange::ACTION_UPDATE)
290 << "Value for key " << key << " doesn't exist yet";
292 dict->SetWithoutPathExpansion(key, value.release());
293 break;
295 case SyncChange::ACTION_DELETE: {
296 DLOG_IF(WARNING, !dict->HasKey(key)) << "Trying to delete nonexistent "
297 << "key " << key;
298 dict->RemoveWithoutPathExpansion(key, NULL);
299 break;
301 case SyncChange::ACTION_INVALID: {
302 NOTREACHED();
303 break;
307 store_->ReportValueChanged(kAtomicSettings,
308 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
309 store_->ReportValueChanged(kSplitSettings,
310 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
311 InformSubscribers();
313 SyncError error;
314 return error;
317 void SupervisedUserSettingsService::OnPrefValueChanged(const std::string& key) {
320 void SupervisedUserSettingsService::OnInitializationCompleted(bool success) {
321 if (!success) {
322 // If this happens, it means the profile directory was not found. There is
323 // not much we can do, but the whole profile will probably be useless
324 // anyway. Just mark initialization as failed and continue otherwise,
325 // because subscribers might still expect to be called back.
326 initialization_failed_ = true;
329 // TODO(bauerb): Temporary CHECK while investigating https://crbug.com/425785.
330 // Remove (or change back to DCHECK) once the bug is fixed.
331 CHECK(IsReady());
332 InformSubscribers();
335 base::DictionaryValue* SupervisedUserSettingsService::GetOrCreateDictionary(
336 const std::string& key) const {
337 base::Value* value = NULL;
338 base::DictionaryValue* dict = NULL;
339 if (store_->GetMutableValue(key, &value)) {
340 bool success = value->GetAsDictionary(&dict);
341 DCHECK(success);
342 } else {
343 dict = new base::DictionaryValue;
344 store_->SetValue(key, make_scoped_ptr(dict),
345 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
348 return dict;
351 base::DictionaryValue*
352 SupervisedUserSettingsService::GetAtomicSettings() const {
353 return GetOrCreateDictionary(kAtomicSettings);
356 base::DictionaryValue* SupervisedUserSettingsService::GetSplitSettings() const {
357 return GetOrCreateDictionary(kSplitSettings);
360 base::DictionaryValue* SupervisedUserSettingsService::GetQueuedItems() const {
361 return GetOrCreateDictionary(kQueuedItems);
364 base::DictionaryValue* SupervisedUserSettingsService::GetDictionaryAndSplitKey(
365 std::string* key) const {
366 size_t pos = key->find_first_of(kSplitSettingKeySeparator);
367 if (pos == std::string::npos)
368 return GetAtomicSettings();
370 base::DictionaryValue* split_settings = GetSplitSettings();
371 std::string prefix = key->substr(0, pos);
372 base::DictionaryValue* dict = NULL;
373 if (!split_settings->GetDictionary(prefix, &dict)) {
374 dict = new base::DictionaryValue;
375 DCHECK(!split_settings->HasKey(prefix));
376 split_settings->Set(prefix, dict);
378 key->erase(0, pos + 1);
379 return dict;
382 scoped_ptr<base::DictionaryValue> SupervisedUserSettingsService::GetSettings() {
383 DCHECK(IsReady());
384 if (!active_ || initialization_failed_)
385 return scoped_ptr<base::DictionaryValue>();
387 scoped_ptr<base::DictionaryValue> settings(local_settings_->DeepCopy());
389 base::DictionaryValue* atomic_settings = GetAtomicSettings();
390 for (base::DictionaryValue::Iterator it(*atomic_settings); !it.IsAtEnd();
391 it.Advance()) {
392 if (!SettingShouldApplyToPrefs(it.key()))
393 continue;
395 settings->Set(it.key(), it.value().DeepCopy());
398 base::DictionaryValue* split_settings = GetSplitSettings();
399 for (base::DictionaryValue::Iterator it(*split_settings); !it.IsAtEnd();
400 it.Advance()) {
401 if (!SettingShouldApplyToPrefs(it.key()))
402 continue;
404 settings->Set(it.key(), it.value().DeepCopy());
407 return settings.Pass();
410 void SupervisedUserSettingsService::InformSubscribers() {
411 if (!IsReady())
412 return;
414 scoped_ptr<base::DictionaryValue> settings = GetSettings();
415 callback_list_.Notify(settings.get());