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
{
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());
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
),
47 observers_(observers
),
48 sync_type_(sync_type
),
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());
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())
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
);
136 syncer::SyncDataList
SyncStorageBackend::GetAllSyncData(syncer::ModelType type
)
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();
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
;
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()];
188 settings
= new base::DictionaryValue();
189 DCHECK(!settings
->HasKey(data
.key())) << "Duplicate settings for "
190 << data
.extension_id() << "/"
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
);
209 error
= storage
->StartSyncing(
210 EmptyDictionaryValue(),
211 CreateSettingsSyncProcessor(extension_id
).Pass());
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
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()];
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
));
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
)
278 CHECK(sync_processor_
.get());
279 return scoped_ptr
<SettingsSyncProcessor
>(new SettingsSyncProcessor(
280 extension_id
, sync_type_
, sync_processor_
.get()));
283 } // namespace extensions