Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / api / storage / sync_storage_backend.cc
blobffbb52de2ceceb6d024828406143149dbf20f3e6
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/extensions/api/storage/sync_storage_backend.h"
7 #include "base/files/file_enumerator.h"
8 #include "base/logging.h"
9 #include "chrome/browser/extensions/api/storage/settings_sync_processor.h"
10 #include "chrome/browser/extensions/api/storage/settings_sync_util.h"
11 #include "chrome/browser/extensions/api/storage/syncable_settings_storage.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "sync/api/sync_error_factory.h"
15 using content::BrowserThread;
17 namespace extensions {
19 namespace {
21 void AddAllSyncData(const std::string& extension_id,
22 const base::DictionaryValue& src,
23 syncer::ModelType type,
24 syncer::SyncDataList* dst) {
25 for (base::DictionaryValue::Iterator it(src); !it.IsAtEnd(); it.Advance()) {
26 dst->push_back(settings_sync_util::CreateData(
27 extension_id, it.key(), it.value(), type));
31 scoped_ptr<base::DictionaryValue> EmptyDictionaryValue() {
32 return make_scoped_ptr(new base::DictionaryValue());
35 } // namespace
37 SyncStorageBackend::SyncStorageBackend(
38 const scoped_refptr<SettingsStorageFactory>& storage_factory,
39 const base::FilePath& base_path,
40 const SettingsStorageQuotaEnforcer::Limits& quota,
41 const scoped_refptr<SettingsObserverList>& observers,
42 syncer::ModelType sync_type,
43 const syncer::SyncableService::StartSyncFlare& flare)
44 : storage_factory_(storage_factory),
45 base_path_(base_path),
46 quota_(quota),
47 observers_(observers),
48 sync_type_(sync_type),
49 flare_(flare) {
50 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
51 DCHECK(sync_type_ == syncer::EXTENSION_SETTINGS ||
52 sync_type_ == syncer::APP_SETTINGS);
55 SyncStorageBackend::~SyncStorageBackend() {}
57 ValueStore* SyncStorageBackend::GetStorage(const std::string& extension_id) {
58 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
59 return GetOrCreateStorageWithSyncData(extension_id, EmptyDictionaryValue());
62 SyncableSettingsStorage* SyncStorageBackend::GetOrCreateStorageWithSyncData(
63 const std::string& extension_id,
64 scoped_ptr<base::DictionaryValue> sync_data) const {
65 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
67 StorageObjMap::iterator maybe_storage = storage_objs_.find(extension_id);
68 if (maybe_storage != storage_objs_.end()) {
69 return maybe_storage->second.get();
72 scoped_ptr<SettingsStorageQuotaEnforcer> storage(
73 new SettingsStorageQuotaEnforcer(
74 quota_, storage_factory_->Create(base_path_, extension_id)));
76 // It's fine to create the quota enforcer underneath the sync layer, since
77 // sync will only go ahead if each underlying storage operation succeeds.
78 linked_ptr<SyncableSettingsStorage> syncable_storage(
79 new SyncableSettingsStorage(
80 observers_, extension_id, storage.release(), sync_type_, flare_));
81 storage_objs_[extension_id] = syncable_storage;
83 if (sync_processor_.get()) {
84 syncer::SyncError error = syncable_storage->StartSyncing(
85 sync_data.Pass(), CreateSettingsSyncProcessor(extension_id).Pass());
86 if (error.IsSet())
87 syncable_storage->StopSyncing();
89 return syncable_storage.get();
92 void SyncStorageBackend::DeleteStorage(const std::string& extension_id) {
93 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
95 // Clear settings when the extension is uninstalled. Leveldb implementations
96 // will also delete the database from disk when the object is destroyed as a
97 // result of being removed from |storage_objs_|.
99 // TODO(kalman): always GetStorage here (rather than only clearing if it
100 // exists) since the storage area may have been unloaded, but we still want
101 // to clear the data from disk.
102 // However, this triggers http://crbug.com/111072.
103 StorageObjMap::iterator maybe_storage = storage_objs_.find(extension_id);
104 if (maybe_storage == storage_objs_.end())
105 return;
106 maybe_storage->second->Clear();
107 storage_objs_.erase(extension_id);
110 std::set<std::string> SyncStorageBackend::GetKnownExtensionIDs() const {
111 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
112 std::set<std::string> result;
114 // Storage areas can be in-memory as well as on disk. |storage_objs_| will
115 // contain all that are in-memory.
116 for (const auto& storage_obj : storage_objs_) {
117 result.insert(storage_obj.first);
120 // Leveldb databases are directories inside |base_path_|.
121 base::FileEnumerator extension_dirs(
122 base_path_, false, base::FileEnumerator::DIRECTORIES);
123 while (!extension_dirs.Next().empty()) {
124 base::FilePath extension_dir = extension_dirs.GetInfo().GetName();
125 DCHECK(!extension_dir.IsAbsolute());
126 // Extension IDs are created as std::strings so they *should* be ASCII.
127 std::string maybe_as_ascii(extension_dir.MaybeAsASCII());
128 if (!maybe_as_ascii.empty()) {
129 result.insert(maybe_as_ascii);
133 return result;
136 syncer::SyncDataList SyncStorageBackend::GetAllSyncData(syncer::ModelType type)
137 const {
138 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
139 // Ignore the type, it's just for sanity checking; assume that whatever base
140 // path we're constructed with is correct for the sync type.
141 DCHECK(type == syncer::EXTENSION_SETTINGS || type == syncer::APP_SETTINGS);
143 // For all extensions, get all their settings. This has the effect
144 // of bringing in the entire state of extension settings in memory; sad.
145 syncer::SyncDataList all_sync_data;
146 std::set<std::string> known_extension_ids(GetKnownExtensionIDs());
148 for (std::set<std::string>::const_iterator it = known_extension_ids.begin();
149 it != known_extension_ids.end();
150 ++it) {
151 ValueStore::ReadResult maybe_settings =
152 GetOrCreateStorageWithSyncData(*it, EmptyDictionaryValue())->Get();
153 if (maybe_settings->HasError()) {
154 LOG(WARNING) << "Failed to get settings for " << *it << ": "
155 << maybe_settings->error().message;
156 continue;
158 AddAllSyncData(*it, maybe_settings->settings(), type, &all_sync_data);
161 return all_sync_data;
164 syncer::SyncMergeResult SyncStorageBackend::MergeDataAndStartSyncing(
165 syncer::ModelType type,
166 const syncer::SyncDataList& initial_sync_data,
167 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
168 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
169 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
170 DCHECK_EQ(sync_type_, type);
171 DCHECK(!sync_processor_.get());
172 DCHECK(sync_processor.get());
173 DCHECK(sync_error_factory.get());
175 sync_processor_ = sync_processor.Pass();
176 sync_error_factory_ = sync_error_factory.Pass();
178 // Group the initial sync data by extension id.
179 // The raw pointers are safe because ownership of each item is passed to
180 // storage->StartSyncing or GetOrCreateStorageWithSyncData.
181 std::map<std::string, base::DictionaryValue*> grouped_sync_data;
183 for (const syncer::SyncData& sync_data : initial_sync_data) {
184 SettingSyncData data(sync_data);
185 // Yes this really is a reference to a pointer.
186 base::DictionaryValue*& settings = grouped_sync_data[data.extension_id()];
187 if (!settings)
188 settings = new base::DictionaryValue();
189 DCHECK(!settings->HasKey(data.key())) << "Duplicate settings for "
190 << data.extension_id() << "/"
191 << data.key();
192 settings->SetWithoutPathExpansion(data.key(), data.PassValue());
195 // Start syncing all existing storage areas. Any storage areas created in
196 // the future will start being synced as part of the creation process.
197 for (const auto& storage_obj : storage_objs_) {
198 const std::string& extension_id = storage_obj.first;
199 SyncableSettingsStorage* storage = storage_obj.second.get();
201 auto group = grouped_sync_data.find(extension_id);
202 syncer::SyncError error;
203 if (group != grouped_sync_data.end()) {
204 error = storage->StartSyncing(
205 make_scoped_ptr(group->second),
206 CreateSettingsSyncProcessor(extension_id).Pass());
207 grouped_sync_data.erase(group);
208 } else {
209 error = storage->StartSyncing(
210 EmptyDictionaryValue(),
211 CreateSettingsSyncProcessor(extension_id).Pass());
214 if (error.IsSet())
215 storage->StopSyncing();
218 // Eagerly create and init the rest of the storage areas that have sync data.
219 // Under normal circumstances (i.e. not first-time sync) this will be all of
220 // them.
221 for (const auto& group : grouped_sync_data) {
222 GetOrCreateStorageWithSyncData(group.first, make_scoped_ptr(group.second));
225 return syncer::SyncMergeResult(type);
228 syncer::SyncError SyncStorageBackend::ProcessSyncChanges(
229 const tracked_objects::Location& from_here,
230 const syncer::SyncChangeList& sync_changes) {
231 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
232 DCHECK(sync_processor_.get());
234 // Group changes by extension, to pass all changes in a single method call.
235 // The raw pointers are safe because ownership of each item is passed to
236 // storage->ProcessSyncChanges.
237 std::map<std::string, SettingSyncDataList*> grouped_sync_data;
239 for (const syncer::SyncChange& change : sync_changes) {
240 scoped_ptr<SettingSyncData> data(new SettingSyncData(change));
241 SettingSyncDataList*& group = grouped_sync_data[data->extension_id()];
242 if (!group)
243 group = new SettingSyncDataList();
244 group->push_back(data.Pass());
247 // Create any storage areas that don't exist yet but have sync data.
248 for (const auto& group : grouped_sync_data) {
249 SyncableSettingsStorage* storage =
250 GetOrCreateStorageWithSyncData(group.first, EmptyDictionaryValue());
251 syncer::SyncError error =
252 storage->ProcessSyncChanges(make_scoped_ptr(group.second));
253 if (error.IsSet())
254 storage->StopSyncing();
257 return syncer::SyncError();
260 void SyncStorageBackend::StopSyncing(syncer::ModelType type) {
261 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
262 DCHECK(type == syncer::EXTENSION_SETTINGS || type == syncer::APP_SETTINGS);
263 DCHECK_EQ(sync_type_, type);
265 for (const auto& storage_obj : storage_objs_) {
266 // Some storage areas may have already stopped syncing if they had areas
267 // and syncing was disabled, but StopSyncing is safe to call multiple times.
268 storage_obj.second->StopSyncing();
271 sync_processor_.reset();
272 sync_error_factory_.reset();
275 scoped_ptr<SettingsSyncProcessor>
276 SyncStorageBackend::CreateSettingsSyncProcessor(const std::string& extension_id)
277 const {
278 CHECK(sync_processor_.get());
279 return scoped_ptr<SettingsSyncProcessor>(new SettingsSyncProcessor(
280 extension_id, sync_type_, sync_processor_.get()));
283 } // namespace extensions