Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / supervised_user / supervised_user_settings_service.cc
blobef3207b1ee0fc70d72be65b55e2096f63bfc2dc3
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), local_settings_(new base::DictionaryValue) {}
55 SupervisedUserSettingsService::~SupervisedUserSettingsService() {}
57 void SupervisedUserSettingsService::Init(
58 base::FilePath profile_path,
59 base::SequencedTaskRunner* sequenced_task_runner,
60 bool load_synchronously) {
61 base::FilePath path =
62 profile_path.Append(chrome::kSupervisedUserSettingsFilename);
63 PersistentPrefStore* store = new JsonPrefStore(
64 path, sequenced_task_runner, scoped_ptr<PrefFilter>());
65 Init(store);
66 if (load_synchronously)
67 store_->ReadPrefs();
68 else
69 store_->ReadPrefsAsync(NULL);
72 void SupervisedUserSettingsService::Init(
73 scoped_refptr<PersistentPrefStore> store) {
74 DCHECK(!store_.get());
75 store_ = store;
76 store_->AddObserver(this);
79 void SupervisedUserSettingsService::Subscribe(
80 const SettingsCallback& callback) {
81 if (IsReady()) {
82 scoped_ptr<base::DictionaryValue> settings = GetSettings();
83 callback.Run(settings.get());
86 subscribers_.push_back(callback);
89 void SupervisedUserSettingsService::SetActive(bool active) {
90 active_ = active;
91 InformSubscribers();
94 bool SupervisedUserSettingsService::IsReady() {
95 return store_->IsInitializationComplete();
98 void SupervisedUserSettingsService::Clear() {
99 store_->RemoveValue(kAtomicSettings);
100 store_->RemoveValue(kSplitSettings);
103 // static
104 std::string SupervisedUserSettingsService::MakeSplitSettingKey(
105 const std::string& prefix,
106 const std::string& key) {
107 return prefix + kSplitSettingKeySeparator + key;
110 void SupervisedUserSettingsService::UploadItem(const std::string& key,
111 scoped_ptr<base::Value> value) {
112 DCHECK(!SettingShouldApplyToPrefs(key));
114 std::string key_suffix = key;
115 base::DictionaryValue* dict = NULL;
116 if (sync_processor_) {
117 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Syncing"));
118 dict = GetDictionaryAndSplitKey(&key_suffix);
119 DCHECK(GetQueuedItems()->empty());
120 SyncChangeList change_list;
121 SyncData data = CreateSyncDataForSetting(key, *value);
122 SyncChange::SyncChangeType change_type =
123 dict->HasKey(key_suffix) ? SyncChange::ACTION_UPDATE
124 : SyncChange::ACTION_ADD;
125 change_list.push_back(SyncChange(FROM_HERE, change_type, data));
126 SyncError error =
127 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
128 DCHECK(!error.IsSet()) << error.ToString();
129 } else {
130 // Queue the item up to be uploaded when we start syncing
131 // (in MergeDataAndStartSyncing()).
132 content::RecordAction(UserMetricsAction("ManagedUsers_UploadItem_Queued"));
133 dict = GetQueuedItems();
135 dict->SetWithoutPathExpansion(key_suffix, value.release());
138 void SupervisedUserSettingsService::SetLocalSettingForTesting(
139 const std::string& key,
140 scoped_ptr<base::Value> value) {
141 if (value)
142 local_settings_->SetWithoutPathExpansion(key, value.release());
143 else
144 local_settings_->RemoveWithoutPathExpansion(key, NULL);
146 InformSubscribers();
149 // static
150 SyncData SupervisedUserSettingsService::CreateSyncDataForSetting(
151 const std::string& name,
152 const base::Value& value) {
153 std::string json_value;
154 base::JSONWriter::Write(&value, &json_value);
155 ::sync_pb::EntitySpecifics specifics;
156 specifics.mutable_managed_user_setting()->set_name(name);
157 specifics.mutable_managed_user_setting()->set_value(json_value);
158 return SyncData::CreateLocalData(name, name, specifics);
161 void SupervisedUserSettingsService::Shutdown() {
162 store_->RemoveObserver(this);
165 SyncMergeResult SupervisedUserSettingsService::MergeDataAndStartSyncing(
166 ModelType type,
167 const SyncDataList& initial_sync_data,
168 scoped_ptr<SyncChangeProcessor> sync_processor,
169 scoped_ptr<SyncErrorFactory> error_handler) {
170 DCHECK_EQ(SUPERVISED_USER_SETTINGS, type);
171 sync_processor_ = sync_processor.Pass();
172 error_handler_ = error_handler.Pass();
174 // Clear all atomic and split settings, then recreate them from Sync data.
175 Clear();
176 for (const SyncData& sync_data : initial_sync_data) {
177 DCHECK_EQ(SUPERVISED_USER_SETTINGS, sync_data.GetDataType());
178 const ::sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
179 sync_data.GetSpecifics().managed_user_setting();
180 scoped_ptr<base::Value> value(
181 JSONReader::Read(supervised_user_setting.value()));
182 std::string name_suffix = supervised_user_setting.name();
183 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&name_suffix);
184 dict->SetWithoutPathExpansion(name_suffix, value.release());
186 store_->ReportValueChanged(kAtomicSettings);
187 store_->ReportValueChanged(kSplitSettings);
188 InformSubscribers();
190 // Upload all the queued up items (either with an ADD or an UPDATE action,
191 // depending on whether they already exist) and move them to split settings.
192 SyncChangeList change_list;
193 base::DictionaryValue* queued_items = GetQueuedItems();
194 for (base::DictionaryValue::Iterator it(*queued_items); !it.IsAtEnd();
195 it.Advance()) {
196 std::string key_suffix = it.key();
197 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&key_suffix);
198 SyncData data = CreateSyncDataForSetting(it.key(), it.value());
199 SyncChange::SyncChangeType change_type =
200 dict->HasKey(key_suffix) ? SyncChange::ACTION_UPDATE
201 : SyncChange::ACTION_ADD;
202 change_list.push_back(SyncChange(FROM_HERE, change_type, data));
203 dict->SetWithoutPathExpansion(key_suffix, it.value().DeepCopy());
205 queued_items->Clear();
207 SyncMergeResult result(SUPERVISED_USER_SETTINGS);
208 // Process all the accumulated changes from the queued items.
209 if (change_list.size() > 0) {
210 store_->ReportValueChanged(kQueuedItems);
211 result.set_error(
212 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list));
215 // TODO(bauerb): Statistics?
216 return result;
219 void SupervisedUserSettingsService::StopSyncing(ModelType type) {
220 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS, type);
221 sync_processor_.reset();
222 error_handler_.reset();
225 SyncDataList SupervisedUserSettingsService::GetAllSyncData(
226 ModelType type) const {
227 DCHECK_EQ(syncer::SUPERVISED_USER_SETTINGS, type);
228 SyncDataList data;
229 for (base::DictionaryValue::Iterator it(*GetAtomicSettings()); !it.IsAtEnd();
230 it.Advance()) {
231 data.push_back(CreateSyncDataForSetting(it.key(), it.value()));
233 for (base::DictionaryValue::Iterator it(*GetSplitSettings()); !it.IsAtEnd();
234 it.Advance()) {
235 const base::DictionaryValue* dict = NULL;
236 it.value().GetAsDictionary(&dict);
237 for (base::DictionaryValue::Iterator jt(*dict);
238 !jt.IsAtEnd(); jt.Advance()) {
239 data.push_back(CreateSyncDataForSetting(
240 MakeSplitSettingKey(it.key(), jt.key()), jt.value()));
243 DCHECK_EQ(0u, GetQueuedItems()->size());
244 return data;
247 SyncError SupervisedUserSettingsService::ProcessSyncChanges(
248 const tracked_objects::Location& from_here,
249 const SyncChangeList& change_list) {
250 for (const SyncChange& sync_change : change_list) {
251 SyncData data = sync_change.sync_data();
252 DCHECK_EQ(SUPERVISED_USER_SETTINGS, data.GetDataType());
253 const ::sync_pb::ManagedUserSettingSpecifics& supervised_user_setting =
254 data.GetSpecifics().managed_user_setting();
255 std::string key = supervised_user_setting.name();
256 base::DictionaryValue* dict = GetDictionaryAndSplitKey(&key);
257 SyncChange::SyncChangeType change_type = sync_change.change_type();
258 switch (change_type) {
259 case SyncChange::ACTION_ADD:
260 case SyncChange::ACTION_UPDATE: {
261 scoped_ptr<base::Value> value(
262 JSONReader::Read(supervised_user_setting.value()));
263 if (dict->HasKey(key)) {
264 DLOG_IF(WARNING, change_type == SyncChange::ACTION_ADD)
265 << "Value for key " << key << " already exists";
266 } else {
267 DLOG_IF(WARNING, change_type == SyncChange::ACTION_UPDATE)
268 << "Value for key " << key << " doesn't exist yet";
270 dict->SetWithoutPathExpansion(key, value.release());
271 break;
273 case SyncChange::ACTION_DELETE: {
274 DLOG_IF(WARNING, !dict->HasKey(key)) << "Trying to delete nonexistent "
275 << "key " << key;
276 dict->RemoveWithoutPathExpansion(key, NULL);
277 break;
279 case SyncChange::ACTION_INVALID: {
280 NOTREACHED();
281 break;
285 store_->ReportValueChanged(kAtomicSettings);
286 store_->ReportValueChanged(kSplitSettings);
287 InformSubscribers();
289 SyncError error;
290 return error;
293 void SupervisedUserSettingsService::OnPrefValueChanged(const std::string& key) {
296 void SupervisedUserSettingsService::OnInitializationCompleted(bool success) {
297 DCHECK(success);
298 DCHECK(IsReady());
299 InformSubscribers();
302 base::DictionaryValue* SupervisedUserSettingsService::GetOrCreateDictionary(
303 const std::string& key) const {
304 base::Value* value = NULL;
305 base::DictionaryValue* dict = NULL;
306 if (store_->GetMutableValue(key, &value)) {
307 bool success = value->GetAsDictionary(&dict);
308 DCHECK(success);
309 } else {
310 dict = new base::DictionaryValue;
311 store_->SetValue(key, dict);
314 return dict;
317 base::DictionaryValue*
318 SupervisedUserSettingsService::GetAtomicSettings() const {
319 return GetOrCreateDictionary(kAtomicSettings);
322 base::DictionaryValue* SupervisedUserSettingsService::GetSplitSettings() const {
323 return GetOrCreateDictionary(kSplitSettings);
326 base::DictionaryValue* SupervisedUserSettingsService::GetQueuedItems() const {
327 return GetOrCreateDictionary(kQueuedItems);
330 base::DictionaryValue* SupervisedUserSettingsService::GetDictionaryAndSplitKey(
331 std::string* key) const {
332 size_t pos = key->find_first_of(kSplitSettingKeySeparator);
333 if (pos == std::string::npos)
334 return GetAtomicSettings();
336 base::DictionaryValue* split_settings = GetSplitSettings();
337 std::string prefix = key->substr(0, pos);
338 base::DictionaryValue* dict = NULL;
339 if (!split_settings->GetDictionary(prefix, &dict)) {
340 dict = new base::DictionaryValue;
341 DCHECK(!split_settings->HasKey(prefix));
342 split_settings->Set(prefix, dict);
344 key->erase(0, pos + 1);
345 return dict;
348 scoped_ptr<base::DictionaryValue> SupervisedUserSettingsService::GetSettings() {
349 DCHECK(IsReady());
350 if (!active_)
351 return scoped_ptr<base::DictionaryValue>();
353 scoped_ptr<base::DictionaryValue> settings(local_settings_->DeepCopy());
355 base::DictionaryValue* atomic_settings = GetAtomicSettings();
356 for (base::DictionaryValue::Iterator it(*atomic_settings); !it.IsAtEnd();
357 it.Advance()) {
358 if (!SettingShouldApplyToPrefs(it.key()))
359 continue;
361 settings->Set(it.key(), it.value().DeepCopy());
364 base::DictionaryValue* split_settings = GetSplitSettings();
365 for (base::DictionaryValue::Iterator it(*split_settings); !it.IsAtEnd();
366 it.Advance()) {
367 if (!SettingShouldApplyToPrefs(it.key()))
368 continue;
370 settings->Set(it.key(), it.value().DeepCopy());
373 return settings.Pass();
376 void SupervisedUserSettingsService::InformSubscribers() {
377 if (!IsReady())
378 return;
380 scoped_ptr<base::DictionaryValue> settings = GetSettings();
381 for (const auto& callback : subscribers_)
382 callback.Run(settings.get());