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
));
33 SyncStorageBackend::SyncStorageBackend(
34 const scoped_refptr
<SettingsStorageFactory
>& storage_factory
,
35 const base::FilePath
& base_path
,
36 const SettingsStorageQuotaEnforcer::Limits
& quota
,
37 const scoped_refptr
<SettingsObserverList
>& observers
,
38 syncer::ModelType sync_type
,
39 const syncer::SyncableService::StartSyncFlare
& flare
)
40 : storage_factory_(storage_factory
),
41 base_path_(base_path
),
43 observers_(observers
),
44 sync_type_(sync_type
),
46 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
47 DCHECK(sync_type_
== syncer::EXTENSION_SETTINGS
||
48 sync_type_
== syncer::APP_SETTINGS
);
51 SyncStorageBackend::~SyncStorageBackend() {}
53 ValueStore
* SyncStorageBackend::GetStorage(const std::string
& extension_id
) {
54 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
55 base::DictionaryValue empty
;
56 return GetOrCreateStorageWithSyncData(extension_id
, empty
);
59 SyncableSettingsStorage
* SyncStorageBackend::GetOrCreateStorageWithSyncData(
60 const std::string
& extension_id
,
61 const base::DictionaryValue
& sync_data
) const {
62 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
64 StorageObjMap::iterator maybe_storage
= storage_objs_
.find(extension_id
);
65 if (maybe_storage
!= storage_objs_
.end()) {
66 return maybe_storage
->second
.get();
69 scoped_ptr
<SettingsStorageQuotaEnforcer
> storage(
70 new SettingsStorageQuotaEnforcer(
71 quota_
, storage_factory_
->Create(base_path_
, extension_id
)));
73 // It's fine to create the quota enforcer underneath the sync layer, since
74 // sync will only go ahead if each underlying storage operation succeeds.
75 linked_ptr
<SyncableSettingsStorage
> syncable_storage(
76 new SyncableSettingsStorage(
77 observers_
, extension_id
, storage
.release(), sync_type_
, flare_
));
78 storage_objs_
[extension_id
] = syncable_storage
;
80 if (sync_processor_
.get()) {
81 syncer::SyncError error
= syncable_storage
->StartSyncing(
82 sync_data
, CreateSettingsSyncProcessor(extension_id
).Pass());
84 syncable_storage
.get()->StopSyncing();
86 return syncable_storage
.get();
89 void SyncStorageBackend::DeleteStorage(const std::string
& extension_id
) {
90 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
92 // Clear settings when the extension is uninstalled. Leveldb implementations
93 // will also delete the database from disk when the object is destroyed as a
94 // result of being removed from |storage_objs_|.
96 // TODO(kalman): always GetStorage here (rather than only clearing if it
97 // exists) since the storage area may have been unloaded, but we still want
98 // to clear the data from disk.
99 // However, this triggers http://crbug.com/111072.
100 StorageObjMap::iterator maybe_storage
= storage_objs_
.find(extension_id
);
101 if (maybe_storage
== storage_objs_
.end())
103 maybe_storage
->second
->Clear();
104 storage_objs_
.erase(extension_id
);
107 std::set
<std::string
> SyncStorageBackend::GetKnownExtensionIDs() const {
108 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
109 std::set
<std::string
> result
;
111 // Storage areas can be in-memory as well as on disk. |storage_objs_| will
112 // contain all that are in-memory.
113 for (StorageObjMap::iterator it
= storage_objs_
.begin();
114 it
!= storage_objs_
.end();
116 result
.insert(it
->first
);
119 // Leveldb databases are directories inside |base_path_|.
120 base::FileEnumerator
extension_dirs(
121 base_path_
, false, base::FileEnumerator::DIRECTORIES
);
122 while (!extension_dirs
.Next().empty()) {
123 base::FilePath extension_dir
= extension_dirs
.GetInfo().GetName();
124 DCHECK(!extension_dir
.IsAbsolute());
125 // Extension IDs are created as std::strings so they *should* be ASCII.
126 std::string
maybe_as_ascii(extension_dir
.MaybeAsASCII());
127 if (!maybe_as_ascii
.empty()) {
128 result
.insert(maybe_as_ascii
);
135 syncer::SyncDataList
SyncStorageBackend::GetAllSyncData(syncer::ModelType type
)
137 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
138 // Ignore the type, it's just for sanity checking; assume that whatever base
139 // path we're constructed with is correct for the sync type.
140 DCHECK(type
== syncer::EXTENSION_SETTINGS
|| type
== syncer::APP_SETTINGS
);
142 // For all extensions, get all their settings. This has the effect
143 // of bringing in the entire state of extension settings in memory; sad.
144 syncer::SyncDataList all_sync_data
;
145 std::set
<std::string
> known_extension_ids(GetKnownExtensionIDs());
147 for (std::set
<std::string
>::const_iterator it
= known_extension_ids
.begin();
148 it
!= known_extension_ids
.end();
150 ValueStore::ReadResult maybe_settings
=
151 GetOrCreateStorageWithSyncData(*it
, base::DictionaryValue())->Get();
152 if (maybe_settings
->HasError()) {
153 LOG(WARNING
) << "Failed to get settings for " << *it
<< ": "
154 << maybe_settings
->error().message
;
157 AddAllSyncData(*it
, maybe_settings
->settings(), type
, &all_sync_data
);
160 return all_sync_data
;
163 syncer::SyncMergeResult
SyncStorageBackend::MergeDataAndStartSyncing(
164 syncer::ModelType type
,
165 const syncer::SyncDataList
& initial_sync_data
,
166 scoped_ptr
<syncer::SyncChangeProcessor
> sync_processor
,
167 scoped_ptr
<syncer::SyncErrorFactory
> sync_error_factory
) {
168 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
169 DCHECK_EQ(sync_type_
, type
);
170 DCHECK(!sync_processor_
.get());
171 DCHECK(sync_processor
.get());
172 DCHECK(sync_error_factory
.get());
174 sync_processor_
= sync_processor
.Pass();
175 sync_error_factory_
= sync_error_factory
.Pass();
177 // Group the initial sync data by extension id.
178 std::map
<std::string
, linked_ptr
<base::DictionaryValue
> > grouped_sync_data
;
179 for (syncer::SyncDataList::const_iterator it
= initial_sync_data
.begin();
180 it
!= initial_sync_data
.end();
182 SettingSyncData
data(*it
);
183 linked_ptr
<base::DictionaryValue
> sync_data
=
184 grouped_sync_data
[data
.extension_id()];
185 if (!sync_data
.get()) {
187 linked_ptr
<base::DictionaryValue
>(new base::DictionaryValue());
188 grouped_sync_data
[data
.extension_id()] = sync_data
;
190 DCHECK(!sync_data
->HasKey(data
.key())) << "Duplicate settings for "
191 << data
.extension_id() << "/"
193 sync_data
->SetWithoutPathExpansion(data
.key(), data
.value().DeepCopy());
196 // Start syncing all existing storage areas. Any storage areas created in
197 // the future will start being synced as part of the creation process.
198 for (StorageObjMap::iterator it
= storage_objs_
.begin();
199 it
!= storage_objs_
.end();
201 std::map
<std::string
, linked_ptr
<base::DictionaryValue
> >::iterator
202 maybe_sync_data
= grouped_sync_data
.find(it
->first
);
203 syncer::SyncError error
;
204 if (maybe_sync_data
!= grouped_sync_data
.end()) {
205 error
= it
->second
->StartSyncing(
206 *maybe_sync_data
->second
,
207 CreateSettingsSyncProcessor(it
->first
).Pass());
208 grouped_sync_data
.erase(it
->first
);
210 base::DictionaryValue empty
;
211 error
= it
->second
->StartSyncing(
212 empty
, CreateSettingsSyncProcessor(it
->first
).Pass());
215 it
->second
->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 (std::map
<std::string
, linked_ptr
<base::DictionaryValue
> >::iterator it
=
222 grouped_sync_data
.begin();
223 it
!= grouped_sync_data
.end();
225 GetOrCreateStorageWithSyncData(it
->first
, *it
->second
);
228 return syncer::SyncMergeResult(type
);
231 syncer::SyncError
SyncStorageBackend::ProcessSyncChanges(
232 const tracked_objects::Location
& from_here
,
233 const syncer::SyncChangeList
& sync_changes
) {
234 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
235 DCHECK(sync_processor_
.get());
237 // Group changes by extension, to pass all changes in a single method call.
238 std::map
<std::string
, SettingSyncDataList
> grouped_sync_data
;
239 for (syncer::SyncChangeList::const_iterator it
= sync_changes
.begin();
240 it
!= sync_changes
.end();
242 SettingSyncData
data(*it
);
243 grouped_sync_data
[data
.extension_id()].push_back(data
);
246 // Create any storage areas that don't exist yet but have sync data.
247 base::DictionaryValue empty
;
248 for (std::map
<std::string
, SettingSyncDataList
>::iterator it
=
249 grouped_sync_data
.begin();
250 it
!= grouped_sync_data
.end();
252 SyncableSettingsStorage
* storage
=
253 GetOrCreateStorageWithSyncData(it
->first
, empty
);
254 syncer::SyncError error
= storage
->ProcessSyncChanges(it
->second
);
256 storage
->StopSyncing();
259 return syncer::SyncError();
262 void SyncStorageBackend::StopSyncing(syncer::ModelType type
) {
263 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
264 DCHECK(type
== syncer::EXTENSION_SETTINGS
|| type
== syncer::APP_SETTINGS
);
265 DCHECK_EQ(sync_type_
, type
);
267 for (StorageObjMap::iterator it
= storage_objs_
.begin();
268 it
!= storage_objs_
.end();
270 // Some storage areas may have already stopped syncing if they had areas
271 // and syncing was disabled, but StopSyncing is safe to call multiple times.
272 it
->second
->StopSyncing();
275 sync_processor_
.reset();
276 sync_error_factory_
.reset();
279 scoped_ptr
<SettingsSyncProcessor
>
280 SyncStorageBackend::CreateSettingsSyncProcessor(const std::string
& extension_id
)
282 CHECK(sync_processor_
.get());
283 return scoped_ptr
<SettingsSyncProcessor
>(new SettingsSyncProcessor(
284 extension_id
, sync_type_
, sync_processor_
.get()));
287 } // namespace extensions