Changes for start_with_ext Telemetry test to work on Windows.
[chromium-blink-merge.git] / chrome / browser / supervised_user / supervised_user_settings_service.cc
blob40eb8ac3def658317f9f666a9de95949246d9757
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()
54 : active_(false),
55 initialization_failed_(false),
56 local_settings_(new base::DictionaryValue) {
59 SupervisedUserSettingsService::~SupervisedUserSettingsService() {}
61 void SupervisedUserSettingsService::Init(
62 base::FilePath profile_path,
63 base::SequencedTaskRunner* sequenced_task_runner,
64 bool load_synchronously) {
65 base::FilePath path =
66 profile_path.Append(chrome::kSupervisedUserSettingsFilename);
67 PersistentPrefStore* store = new JsonPrefStore(
68 path, sequenced_task_runner, scoped_ptr<PrefFilter>());
69 Init(store);
70 if (load_synchronously) {
71 store_->ReadPrefs();
72 // TODO(bauerb): Temporary CHECK while investigating
73 // https://crbug.com/425785. Remove (or change to DCHECK) once the bug
74 // is fixed.
75 CHECK(store_->IsInitializationComplete());
76 } else {
77 store_->ReadPrefsAsync(NULL);
81 void SupervisedUserSettingsService::Init(
82 scoped_refptr<PersistentPrefStore> store) {
83 DCHECK(!store_.get());
84 store_ = store;
85 store_->AddObserver(this);
88 void SupervisedUserSettingsService::Subscribe(
89 const SettingsCallback& callback) {
90 if (IsReady()) {
91 scoped_ptr<base::DictionaryValue> settings = GetSettings();
92 callback.Run(settings.get());
95 subscribers_.push_back(callback);
98 void SupervisedUserSettingsService::SetActive(bool active) {
99 active_ = active;
100 InformSubscribers();
103 bool SupervisedUserSettingsService::IsReady() {
104 // Initialization cannot be complete but have failed at the same time.
105 DCHECK(!(store_->IsInitializationComplete() && initialization_failed_));
106 return initialization_failed_ || store_->IsInitializationComplete();
109 void SupervisedUserSettingsService::Clear() {
110 store_->RemoveValue(kAtomicSettings,
111 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
112 store_->RemoveValue(kSplitSettings,
113 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
116 // static
117 std::string SupervisedUserSettingsService::MakeSplitSettingKey(
118 const std::string& prefix,
119 const std::string& key) {
120 return prefix + kSplitSettingKeySeparator + key;
123 void SupervisedUserSettingsService::UploadItem(const std::string& key,
124 scoped_ptr<base::Value> value) {
125 DCHECK(!SettingShouldApplyToPrefs(key));
127 std::string key_suffix = key;
128 base::DictionaryValue* dict = NULL;
129 if (sync_processor_) {
130 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Syncing"));
131 dict = GetDictionaryAndSplitKey(&key_suffix);
132 DCHECK(GetQueuedItems()->empty());
133 SyncChangeList change_list;
134 SyncData data = CreateSyncDataForSetting(key, *value);
135 SyncChange::SyncChangeType change_type =
136 dict->HasKey(key_suffix) ? SyncChange::ACTION_UPDATE
137 : SyncChange::ACTION_ADD;
138 change_list.push_back(SyncChange(FROM_HERE, change_type, data));
139 SyncError error =
140 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
141 DCHECK(!error.IsSet()) << error.ToString();
142 } else {
143 // Queue the item up to be uploaded when we start syncing
144 // (in MergeDataAndStartSyncing()).
145 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Queued"));
146 dict = GetQueuedItems();
148 dict->SetWithoutPathExpansion(key_suffix, value.release());
151 void SupervisedUserSettingsService::SetLocalSetting(
152 const std::string& key,
153 scoped_ptr<base::Value> value) {
154 if (value)
155 local_settings_->SetWithoutPathExpansion(key, value.release());
156 else
157 local_settings_->RemoveWithoutPathExpansion(key, NULL);
159 InformSubscribers();
162 // static
163 SyncData SupervisedUserSettingsService::CreateSyncDataForSetting(
164 const std::string& name,
165 const base::Value& value) {
166 std::string json_value;
167 base::JSONWriter::Write(value, &json_value);
168 ::sync_pb::EntitySpecifics specifics;
169 specifics.mutable_managed_user_setting()->set_name(name);
170 specifics.mutable_managed_user_setting()->set_value(json_value);
171 return SyncData::CreateLocalData(name, name, specifics);
174 void SupervisedUserSettingsService::Shutdown() {
175 store_->RemoveObserver(this);
178 SyncMergeResult SupervisedUserSettingsService::MergeDataAndStartSyncing(
179 ModelType type,
180 const SyncDataList& initial_sync_data,
181 scoped_ptr<SyncChangeProcessor> sync_processor,
182 scoped_ptr<SyncErrorFactory> error_handler) {
183 DCHECK_EQ(SUPERVISED_USER_SETTINGS, type);
184 sync_processor_ = sync_processor.Pass();
185 error_handler_ = error_handler.Pass();
187 // Clear all atomic and split settings, then recreate them from Sync data.
188 Clear();
189 for (const SyncData& sync_data : initial_sync_data) {
190 DCHECK_EQ(SUPERVISED_USER_SETTINGS, sync_data.GetDataType());
191 const ::sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
192 sync_data.GetSpecifics().managed_user_setting();
193 scoped_ptr<base::Value> value(
194 JSONReader::DeprecatedRead(supervised_user_setting.value()));
195 std::string name_suffix = supervised_user_setting.name();
196 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&name_suffix);
197 dict->SetWithoutPathExpansion(name_suffix, value.release());
199 store_->ReportValueChanged(kAtomicSettings,
200 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
201 store_->ReportValueChanged(kSplitSettings,
202 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
203 InformSubscribers();
205 // Upload all the queued up items (either with an ADD or an UPDATE action,
206 // depending on whether they already exist) and move them to split settings.
207 SyncChangeList change_list;
208 base::DictionaryValue* queued_items = GetQueuedItems();
209 for (base::DictionaryValue::Iterator it(*queued_items); !it.IsAtEnd();
210 it.Advance()) {
211 std::string key_suffix = it.key();
212 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&key_suffix);
213 SyncData data = CreateSyncDataForSetting(it.key(), it.value());
214 SyncChange::SyncChangeType change_type =
215 dict->HasKey(key_suffix) ? SyncChange::ACTION_UPDATE
216 : SyncChange::ACTION_ADD;
217 change_list.push_back(SyncChange(FROM_HERE, change_type, data));
218 dict->SetWithoutPathExpansion(key_suffix, it.value().DeepCopy());
220 queued_items->Clear();
222 SyncMergeResult result(SUPERVISED_USER_SETTINGS);
223 // Process all the accumulated changes from the queued items.
224 if (change_list.size() > 0) {
225 store_->ReportValueChanged(kQueuedItems,
226 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
227 result.set_error(
228 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list));
231 // TODO(bauerb): Statistics?
232 return result;
235 void SupervisedUserSettingsService::StopSyncing(ModelType type) {
236 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS, type);
237 sync_processor_.reset();
238 error_handler_.reset();
241 SyncDataList SupervisedUserSettingsService::GetAllSyncData(
242 ModelType type) const {
243 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS, type);
244 SyncDataList data;
245 for (base::DictionaryValue::Iterator it(*GetAtomicSettings()); !it.IsAtEnd();
246 it.Advance()) {
247 data.push_back(CreateSyncDataForSetting(it.key(), it.value()));
249 for (base::DictionaryValue::Iterator it(*GetSplitSettings()); !it.IsAtEnd();
250 it.Advance()) {
251 const base::DictionaryValue* dict = NULL;
252 it.value().GetAsDictionary(&dict);
253 for (base::DictionaryValue::Iterator jt(*dict);
254 !jt.IsAtEnd(); jt.Advance()) {
255 data.push_back(CreateSyncDataForSetting(
256 MakeSplitSettingKey(it.key(), jt.key()), jt.value()));
259 DCHECK_EQ(0u, GetQueuedItems()->size());
260 return data;
263 SyncError SupervisedUserSettingsService::ProcessSyncChanges(
264 const tracked_objects::Location& from_here,
265 const SyncChangeList& change_list) {
266 for (const SyncChange& sync_change : change_list) {
267 SyncData data = sync_change.sync_data();
268 DCHECK_EQ(SUPERVISED_USER_SETTINGS, data.GetDataType());
269 const ::sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
270 data.GetSpecifics().managed_user_setting();
271 std::string key = supervised_user_setting.name();
272 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&key);
273 SyncChange::SyncChangeType change_type = sync_change.change_type();
274 switch (change_type) {
275 case SyncChange::ACTION_ADD:
276 case SyncChange::ACTION_UPDATE: {
277 scoped_ptr<base::Value> value(
278 JSONReader::DeprecatedRead(supervised_user_setting.value()));
279 if (dict->HasKey(key)) {
280 DLOG_IF(WARNING, change_type == SyncChange::ACTION_ADD)
281 << "Value for key " << key << " already exists";
282 } else {
283 DLOG_IF(WARNING, change_type == SyncChange::ACTION_UPDATE)
284 << "Value for key " << key << " doesn't exist yet";
286 dict->SetWithoutPathExpansion(key, value.release());
287 break;
289 case SyncChange::ACTION_DELETE: {
290 DLOG_IF(WARNING, !dict->HasKey(key)) << "Trying to delete nonexistent "
291 << "key " << key;
292 dict->RemoveWithoutPathExpansion(key, NULL);
293 break;
295 case SyncChange::ACTION_INVALID: {
296 NOTREACHED();
297 break;
301 store_->ReportValueChanged(kAtomicSettings,
302 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
303 store_->ReportValueChanged(kSplitSettings,
304 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
305 InformSubscribers();
307 SyncError error;
308 return error;
311 void SupervisedUserSettingsService::OnPrefValueChanged(const std::string& key) {
314 void SupervisedUserSettingsService::OnInitializationCompleted(bool success) {
315 if (!success) {
316 // If this happens, it means the profile directory was not found. There is
317 // not much we can do, but the whole profile will probably be useless
318 // anyway. Just mark initialization as failed and continue otherwise,
319 // because subscribers might still expect to be called back.
320 initialization_failed_ = true;
323 // TODO(bauerb): Temporary CHECK while investigating https://crbug.com/425785.
324 // Remove (or change back to DCHECK) once the bug is fixed.
325 CHECK(IsReady());
326 InformSubscribers();
329 base::DictionaryValue* SupervisedUserSettingsService::GetOrCreateDictionary(
330 const std::string& key) const {
331 base::Value* value = NULL;
332 base::DictionaryValue* dict = NULL;
333 if (store_->GetMutableValue(key, &value)) {
334 bool success = value->GetAsDictionary(&dict);
335 DCHECK(success);
336 } else {
337 dict = new base::DictionaryValue;
338 store_->SetValue(key, make_scoped_ptr(dict),
339 WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
342 return dict;
345 base::DictionaryValue*
346 SupervisedUserSettingsService::GetAtomicSettings() const {
347 return GetOrCreateDictionary(kAtomicSettings);
350 base::DictionaryValue* SupervisedUserSettingsService::GetSplitSettings() const {
351 return GetOrCreateDictionary(kSplitSettings);
354 base::DictionaryValue* SupervisedUserSettingsService::GetQueuedItems() const {
355 return GetOrCreateDictionary(kQueuedItems);
358 base::DictionaryValue* SupervisedUserSettingsService::GetDictionaryAndSplitKey(
359 std::string* key) const {
360 size_t pos = key->find_first_of(kSplitSettingKeySeparator);
361 if (pos == std::string::npos)
362 return GetAtomicSettings();
364 base::DictionaryValue* split_settings = GetSplitSettings();
365 std::string prefix = key->substr(0, pos);
366 base::DictionaryValue* dict = NULL;
367 if (!split_settings->GetDictionary(prefix, &dict)) {
368 dict = new base::DictionaryValue;
369 DCHECK(!split_settings->HasKey(prefix));
370 split_settings->Set(prefix, dict);
372 key->erase(0, pos + 1);
373 return dict;
376 scoped_ptr<base::DictionaryValue> SupervisedUserSettingsService::GetSettings() {
377 DCHECK(IsReady());
378 if (!active_ || initialization_failed_)
379 return scoped_ptr<base::DictionaryValue>();
381 scoped_ptr<base::DictionaryValue> settings(local_settings_->DeepCopy());
383 base::DictionaryValue* atomic_settings = GetAtomicSettings();
384 for (base::DictionaryValue::Iterator it(*atomic_settings); !it.IsAtEnd();
385 it.Advance()) {
386 if (!SettingShouldApplyToPrefs(it.key()))
387 continue;
389 settings->Set(it.key(), it.value().DeepCopy());
392 base::DictionaryValue* split_settings = GetSplitSettings();
393 for (base::DictionaryValue::Iterator it(*split_settings); !it.IsAtEnd();
394 it.Advance()) {
395 if (!SettingShouldApplyToPrefs(it.key()))
396 continue;
398 settings->Set(it.key(), it.value().DeepCopy());
401 return settings.Pass();
404 void SupervisedUserSettingsService::InformSubscribers() {
405 if (!IsReady())
406 return;
408 scoped_ptr<base::DictionaryValue> settings = GetSettings();
409 for (const auto& callback : subscribers_)
410 callback.Run(settings.get());