Unwind the URL-based experiment IDs.
[chromium-blink-merge.git] / chrome / browser / supervised_user / supervised_user_settings_service.cc
blob69c0acf1fa698be8c3f176ddf2906e4ed9eab010
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 !StartsWithASCII(name, kSupervisedUserInternalItemPrefix, false);
50 } // namespace
52 SupervisedUserSettingsService::SupervisedUserSettingsService()
53 : active_(false),
54 initialization_failed_(false),
55 local_settings_(new base::DictionaryValue) {
58 SupervisedUserSettingsService::~SupervisedUserSettingsService() {}
60 void SupervisedUserSettingsService::Init(
61 base::FilePath profile_path,
62 base::SequencedTaskRunner* sequenced_task_runner,
63 bool load_synchronously) {
64 base::FilePath path =
65 profile_path.Append(chrome::kSupervisedUserSettingsFilename);
66 PersistentPrefStore* store = new JsonPrefStore(
67 path, sequenced_task_runner, scoped_ptr<PrefFilter>());
68 Init(store);
69 if (load_synchronously) {
70 store_->ReadPrefs();
71 // TODO(bauerb): Temporary CHECK while investigating
72 // https://crbug.com/425785. Remove (or change to DCHECK) once the bug
73 // is fixed.
74 CHECK(store_->IsInitializationComplete());
75 } else {
76 store_->ReadPrefsAsync(NULL);
80 void SupervisedUserSettingsService::Init(
81 scoped_refptr<PersistentPrefStore> store) {
82 DCHECK(!store_.get());
83 store_ = store;
84 store_->AddObserver(this);
87 void SupervisedUserSettingsService::Subscribe(
88 const SettingsCallback& callback) {
89 if (IsReady()) {
90 scoped_ptr<base::DictionaryValue> settings = GetSettings();
91 callback.Run(settings.get());
94 subscribers_.push_back(callback);
97 void SupervisedUserSettingsService::SetActive(bool active) {
98 active_ = active;
99 InformSubscribers();
102 bool SupervisedUserSettingsService::IsReady() {
103 // Initialization cannot be complete but have failed at the same time.
104 DCHECK(!(store_->IsInitializationComplete() && initialization_failed_));
105 return initialization_failed_ || store_->IsInitializationComplete();
108 void SupervisedUserSettingsService::Clear() {
109 store_->RemoveValue(kAtomicSettings);
110 store_->RemoveValue(kSplitSettings);
113 // static
114 std::string SupervisedUserSettingsService::MakeSplitSettingKey(
115 const std::string& prefix,
116 const std::string& key) {
117 return prefix + kSplitSettingKeySeparator + key;
120 void SupervisedUserSettingsService::UploadItem(const std::string& key,
121 scoped_ptr<base::Value> value) {
122 DCHECK(!SettingShouldApplyToPrefs(key));
124 std::string key_suffix = key;
125 base::DictionaryValue* dict = NULL;
126 if (sync_processor_) {
127 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Syncing"));
128 dict = GetDictionaryAndSplitKey(&key_suffix);
129 DCHECK(GetQueuedItems()->empty());
130 SyncChangeList change_list;
131 SyncData data = CreateSyncDataForSetting(key, *value);
132 SyncChange::SyncChangeType change_type =
133 dict->HasKey(key_suffix) ? SyncChange::ACTION_UPDATE
134 : SyncChange::ACTION_ADD;
135 change_list.push_back(SyncChange(FROM_HERE, change_type, data));
136 SyncError error =
137 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
138 DCHECK(!error.IsSet()) << error.ToString();
139 } else {
140 // Queue the item up to be uploaded when we start syncing
141 // (in MergeDataAndStartSyncing()).
142 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Queued"));
143 dict = GetQueuedItems();
145 dict->SetWithoutPathExpansion(key_suffix, value.release());
148 void SupervisedUserSettingsService::SetLocalSetting(
149 const std::string& key,
150 scoped_ptr<base::Value> value) {
151 if (value)
152 local_settings_->SetWithoutPathExpansion(key, value.release());
153 else
154 local_settings_->RemoveWithoutPathExpansion(key, NULL);
156 InformSubscribers();
159 // static
160 SyncData SupervisedUserSettingsService::CreateSyncDataForSetting(
161 const std::string& name,
162 const base::Value& value) {
163 std::string json_value;
164 base::JSONWriter::Write(&value, &json_value);
165 ::sync_pb::EntitySpecifics specifics;
166 specifics.mutable_managed_user_setting()->set_name(name);
167 specifics.mutable_managed_user_setting()->set_value(json_value);
168 return SyncData::CreateLocalData(name, name, specifics);
171 void SupervisedUserSettingsService::Shutdown() {
172 store_->RemoveObserver(this);
175 SyncMergeResult SupervisedUserSettingsService::MergeDataAndStartSyncing(
176 ModelType type,
177 const SyncDataList& initial_sync_data,
178 scoped_ptr<SyncChangeProcessor> sync_processor,
179 scoped_ptr<SyncErrorFactory> error_handler) {
180 DCHECK_EQ(SUPERVISED_USER_SETTINGS, type);
181 sync_processor_ = sync_processor.Pass();
182 error_handler_ = error_handler.Pass();
184 // Clear all atomic and split settings, then recreate them from Sync data.
185 Clear();
186 for (const SyncData& sync_data : initial_sync_data) {
187 DCHECK_EQ(SUPERVISED_USER_SETTINGS, sync_data.GetDataType());
188 const ::sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
189 sync_data.GetSpecifics().managed_user_setting();
190 scoped_ptr<base::Value> value(
191 JSONReader::Read(supervised_user_setting.value()));
192 std::string name_suffix = supervised_user_setting.name();
193 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&name_suffix);
194 dict->SetWithoutPathExpansion(name_suffix, value.release());
196 store_->ReportValueChanged(kAtomicSettings);
197 store_->ReportValueChanged(kSplitSettings);
198 InformSubscribers();
200 // Upload all the queued up items (either with an ADD or an UPDATE action,
201 // depending on whether they already exist) and move them to split settings.
202 SyncChangeList change_list;
203 base::DictionaryValue* queued_items = GetQueuedItems();
204 for (base::DictionaryValue::Iterator it(*queued_items); !it.IsAtEnd();
205 it.Advance()) {
206 std::string key_suffix = it.key();
207 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&key_suffix);
208 SyncData data = CreateSyncDataForSetting(it.key(), it.value());
209 SyncChange::SyncChangeType change_type =
210 dict->HasKey(key_suffix) ? SyncChange::ACTION_UPDATE
211 : SyncChange::ACTION_ADD;
212 change_list.push_back(SyncChange(FROM_HERE, change_type, data));
213 dict->SetWithoutPathExpansion(key_suffix, it.value().DeepCopy());
215 queued_items->Clear();
217 SyncMergeResult result(SUPERVISED_USER_SETTINGS);
218 // Process all the accumulated changes from the queued items.
219 if (change_list.size() > 0) {
220 store_->ReportValueChanged(kQueuedItems);
221 result.set_error(
222 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list));
225 // TODO(bauerb): Statistics?
226 return result;
229 void SupervisedUserSettingsService::StopSyncing(ModelType type) {
230 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS, type);
231 sync_processor_.reset();
232 error_handler_.reset();
235 SyncDataList SupervisedUserSettingsService::GetAllSyncData(
236 ModelType type) const {
237 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS, type);
238 SyncDataList data;
239 for (base::DictionaryValue::Iterator it(*GetAtomicSettings()); !it.IsAtEnd();
240 it.Advance()) {
241 data.push_back(CreateSyncDataForSetting(it.key(), it.value()));
243 for (base::DictionaryValue::Iterator it(*GetSplitSettings()); !it.IsAtEnd();
244 it.Advance()) {
245 const base::DictionaryValue* dict = NULL;
246 it.value().GetAsDictionary(&dict);
247 for (base::DictionaryValue::Iterator jt(*dict);
248 !jt.IsAtEnd(); jt.Advance()) {
249 data.push_back(CreateSyncDataForSetting(
250 MakeSplitSettingKey(it.key(), jt.key()), jt.value()));
253 DCHECK_EQ(0u, GetQueuedItems()->size());
254 return data;
257 SyncError SupervisedUserSettingsService::ProcessSyncChanges(
258 const tracked_objects::Location& from_here,
259 const SyncChangeList& change_list) {
260 for (const SyncChange& sync_change : change_list) {
261 SyncData data = sync_change.sync_data();
262 DCHECK_EQ(SUPERVISED_USER_SETTINGS, data.GetDataType());
263 const ::sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
264 data.GetSpecifics().managed_user_setting();
265 std::string key = supervised_user_setting.name();
266 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&key);
267 SyncChange::SyncChangeType change_type = sync_change.change_type();
268 switch (change_type) {
269 case SyncChange::ACTION_ADD:
270 case SyncChange::ACTION_UPDATE: {
271 scoped_ptr<base::Value> value(
272 JSONReader::Read(supervised_user_setting.value()));
273 if (dict->HasKey(key)) {
274 DLOG_IF(WARNING, change_type == SyncChange::ACTION_ADD)
275 << "Value for key " << key << " already exists";
276 } else {
277 DLOG_IF(WARNING, change_type == SyncChange::ACTION_UPDATE)
278 << "Value for key " << key << " doesn't exist yet";
280 dict->SetWithoutPathExpansion(key, value.release());
281 break;
283 case SyncChange::ACTION_DELETE: {
284 DLOG_IF(WARNING, !dict->HasKey(key)) << "Trying to delete nonexistent "
285 << "key " << key;
286 dict->RemoveWithoutPathExpansion(key, NULL);
287 break;
289 case SyncChange::ACTION_INVALID: {
290 NOTREACHED();
291 break;
295 store_->ReportValueChanged(kAtomicSettings);
296 store_->ReportValueChanged(kSplitSettings);
297 InformSubscribers();
299 SyncError error;
300 return error;
303 void SupervisedUserSettingsService::OnPrefValueChanged(const std::string& key) {
306 void SupervisedUserSettingsService::OnInitializationCompleted(bool success) {
307 if (!success) {
308 // If this happens, it means the profile directory was not found. There is
309 // not much we can do, but the whole profile will probably be useless
310 // anyway. Just mark initialization as failed and continue otherwise,
311 // because subscribers might still expect to be called back.
312 initialization_failed_ = true;
315 // TODO(bauerb): Temporary CHECK while investigating https://crbug.com/425785.
316 // Remove (or change back to DCHECK) once the bug is fixed.
317 CHECK(IsReady());
318 InformSubscribers();
321 base::DictionaryValue* SupervisedUserSettingsService::GetOrCreateDictionary(
322 const std::string& key) const {
323 base::Value* value = NULL;
324 base::DictionaryValue* dict = NULL;
325 if (store_->GetMutableValue(key, &value)) {
326 bool success = value->GetAsDictionary(&dict);
327 DCHECK(success);
328 } else {
329 dict = new base::DictionaryValue;
330 store_->SetValue(key, dict);
333 return dict;
336 base::DictionaryValue*
337 SupervisedUserSettingsService::GetAtomicSettings() const {
338 return GetOrCreateDictionary(kAtomicSettings);
341 base::DictionaryValue* SupervisedUserSettingsService::GetSplitSettings() const {
342 return GetOrCreateDictionary(kSplitSettings);
345 base::DictionaryValue* SupervisedUserSettingsService::GetQueuedItems() const {
346 return GetOrCreateDictionary(kQueuedItems);
349 base::DictionaryValue* SupervisedUserSettingsService::GetDictionaryAndSplitKey(
350 std::string* key) const {
351 size_t pos = key->find_first_of(kSplitSettingKeySeparator);
352 if (pos == std::string::npos)
353 return GetAtomicSettings();
355 base::DictionaryValue* split_settings = GetSplitSettings();
356 std::string prefix = key->substr(0, pos);
357 base::DictionaryValue* dict = NULL;
358 if (!split_settings->GetDictionary(prefix, &dict)) {
359 dict = new base::DictionaryValue;
360 DCHECK(!split_settings->HasKey(prefix));
361 split_settings->Set(prefix, dict);
363 key->erase(0, pos + 1);
364 return dict;
367 scoped_ptr<base::DictionaryValue> SupervisedUserSettingsService::GetSettings() {
368 DCHECK(IsReady());
369 if (!active_ || initialization_failed_)
370 return scoped_ptr<base::DictionaryValue>();
372 scoped_ptr<base::DictionaryValue> settings(local_settings_->DeepCopy());
374 base::DictionaryValue* atomic_settings = GetAtomicSettings();
375 for (base::DictionaryValue::Iterator it(*atomic_settings); !it.IsAtEnd();
376 it.Advance()) {
377 if (!SettingShouldApplyToPrefs(it.key()))
378 continue;
380 settings->Set(it.key(), it.value().DeepCopy());
383 base::DictionaryValue* split_settings = GetSplitSettings();
384 for (base::DictionaryValue::Iterator it(*split_settings); !it.IsAtEnd();
385 it.Advance()) {
386 if (!SettingShouldApplyToPrefs(it.key()))
387 continue;
389 settings->Set(it.key(), it.value().DeepCopy());
392 return settings.Pass();
395 void SupervisedUserSettingsService::InformSubscribers() {
396 if (!IsReady())
397 return;
399 scoped_ptr<base::DictionaryValue> settings = GetSettings();
400 for (const auto& callback : subscribers_)
401 callback.Run(settings.get());