1 // Copyright (c) 2012 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/settings_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 SettingsBackend::SettingsBackend(
20 const scoped_refptr<SettingsStorageFactory>& storage_factory,
21 const base::FilePath& base_path,
22 syncer::ModelType sync_type,
23 const syncer::SyncableService::StartSyncFlare& flare,
24 const SettingsStorageQuotaEnforcer::Limits& quota,
25 const scoped_refptr<SettingsObserverList>& observers)
26 : storage_factory_(storage_factory),
27 base_path_(base_path),
29 observers_(observers),
30 sync_type_(sync_type),
32 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
33 DCHECK(sync_type_ == syncer::EXTENSION_SETTINGS ||
34 sync_type_ == syncer::APP_SETTINGS);
37 SettingsBackend::~SettingsBackend() {
38 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
41 ValueStore* SettingsBackend::GetStorage(
42 const std::string& extension_id) const {
43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
44 base::DictionaryValue empty;
45 return GetOrCreateStorageWithSyncData(extension_id, empty);
48 SyncableSettingsStorage* SettingsBackend::GetOrCreateStorageWithSyncData(
49 const std::string& extension_id,
50 const base::DictionaryValue& sync_data) const {
51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
53 StorageObjMap::iterator maybe_storage = storage_objs_.find(extension_id);
54 if (maybe_storage != storage_objs_.end()) {
55 return maybe_storage->second.get();
58 ValueStore* storage = storage_factory_->Create(base_path_, extension_id);
61 // It's fine to create the quota enforcer underneath the sync layer, since
62 // sync will only go ahead if each underlying storage operation succeeds.
63 storage = new SettingsStorageQuotaEnforcer(quota_, storage);
65 linked_ptr<SyncableSettingsStorage> syncable_storage(
66 new SyncableSettingsStorage(
70 storage_objs_[extension_id] = syncable_storage;
72 if (sync_processor_.get()) {
73 syncer::SyncError error =
74 syncable_storage->StartSyncing(
76 CreateSettingsSyncProcessor(extension_id).Pass());
78 syncable_storage.get()->StopSyncing();
80 // Tell sync to try and start soon, because syncable changes to sync_type_
81 // have started happening. This will cause sync to call us back
82 // asynchronously via MergeDataAndStartSyncing as soon as possible.
83 flare_.Run(sync_type_);
86 return syncable_storage.get();
89 void SettingsBackend::DeleteStorage(const std::string& extension_id) {
90 DCHECK(BrowserThread::CurrentlyOn(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> SettingsBackend::GetKnownExtensionIDs() const {
108 DCHECK(BrowserThread::CurrentlyOn(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(); ++it) {
115 result.insert(it->first);
118 // Leveldb databases are directories inside base_path_.
119 base::FileEnumerator extension_dirs(
120 base_path_, false, base::FileEnumerator::DIRECTORIES);
121 while (!extension_dirs.Next().empty()) {
122 base::FilePath extension_dir = extension_dirs.GetInfo().GetName();
123 DCHECK(!extension_dir.IsAbsolute());
124 // Extension IDs are created as std::strings so they *should* be ASCII.
125 std::string maybe_as_ascii(extension_dir.MaybeAsASCII());
126 if (!maybe_as_ascii.empty()) {
127 result.insert(maybe_as_ascii);
134 static void AddAllSyncData(
135 const std::string& extension_id,
136 const base::DictionaryValue& src,
137 syncer::ModelType type,
138 syncer::SyncDataList* dst) {
139 for (base::DictionaryValue::Iterator it(src); !it.IsAtEnd(); it.Advance()) {
140 dst->push_back(settings_sync_util::CreateData(
141 extension_id, it.key(), it.value(), type));
145 syncer::SyncDataList SettingsBackend::GetAllSyncData(
146 syncer::ModelType type) const {
147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
148 // Ignore the type, it's just for sanity checking; assume that whatever base
149 // path we're constructed with is correct for the sync type.
150 DCHECK(type == syncer::EXTENSION_SETTINGS ||
151 type == syncer::APP_SETTINGS);
153 // For all extensions, get all their settings. This has the effect
154 // of bringing in the entire state of extension settings in memory; sad.
155 syncer::SyncDataList all_sync_data;
156 std::set<std::string> known_extension_ids(GetKnownExtensionIDs());
158 for (std::set<std::string>::const_iterator it = known_extension_ids.begin();
159 it != known_extension_ids.end(); ++it) {
160 ValueStore::ReadResult maybe_settings = GetStorage(*it)->Get();
161 if (maybe_settings->HasError()) {
162 LOG(WARNING) << "Failed to get settings for " << *it << ": " <<
163 maybe_settings->error().message;
166 AddAllSyncData(*it, maybe_settings->settings(), type, &all_sync_data);
169 return all_sync_data;
172 syncer::SyncMergeResult SettingsBackend::MergeDataAndStartSyncing(
173 syncer::ModelType type,
174 const syncer::SyncDataList& initial_sync_data,
175 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
176 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
178 DCHECK_EQ(sync_type_, type);
179 DCHECK(!sync_processor_.get());
180 DCHECK(sync_processor.get());
181 DCHECK(sync_error_factory.get());
183 sync_processor_ = sync_processor.Pass();
184 sync_error_factory_ = sync_error_factory.Pass();
186 // Group the initial sync data by extension id.
187 std::map<std::string, linked_ptr<base::DictionaryValue> > grouped_sync_data;
188 for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin();
189 it != initial_sync_data.end(); ++it) {
190 SettingSyncData data(*it);
191 linked_ptr<base::DictionaryValue> sync_data =
192 grouped_sync_data[data.extension_id()];
193 if (!sync_data.get()) {
194 sync_data = linked_ptr<base::DictionaryValue>(
195 new base::DictionaryValue());
196 grouped_sync_data[data.extension_id()] = sync_data;
198 DCHECK(!sync_data->HasKey(data.key())) <<
199 "Duplicate settings for " << data.extension_id() << "/" << data.key();
200 sync_data->SetWithoutPathExpansion(data.key(), data.value().DeepCopy());
203 // Start syncing all existing storage areas. Any storage areas created in
204 // the future will start being synced as part of the creation process.
205 for (StorageObjMap::iterator it = storage_objs_.begin();
206 it != storage_objs_.end(); ++it) {
207 std::map<std::string, linked_ptr<base::DictionaryValue> >::iterator
208 maybe_sync_data = grouped_sync_data.find(it->first);
209 syncer::SyncError error;
210 if (maybe_sync_data != grouped_sync_data.end()) {
211 error = it->second->StartSyncing(
212 *maybe_sync_data->second,
213 CreateSettingsSyncProcessor(it->first).Pass());
214 grouped_sync_data.erase(it->first);
216 base::DictionaryValue empty;
217 error = it->second->StartSyncing(
219 CreateSettingsSyncProcessor(it->first).Pass());
222 it->second->StopSyncing();
225 // Eagerly create and init the rest of the storage areas that have sync data.
226 // Under normal circumstances (i.e. not first-time sync) this will be all of
228 for (std::map<std::string, linked_ptr<base::DictionaryValue> >::iterator it =
229 grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) {
230 GetOrCreateStorageWithSyncData(it->first, *it->second);
233 return syncer::SyncMergeResult(type);
236 syncer::SyncError SettingsBackend::ProcessSyncChanges(
237 const tracked_objects::Location& from_here,
238 const syncer::SyncChangeList& sync_changes) {
239 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
240 DCHECK(sync_processor_.get());
242 // Group changes by extension, to pass all changes in a single method call.
243 std::map<std::string, SettingSyncDataList> grouped_sync_data;
244 for (syncer::SyncChangeList::const_iterator it = sync_changes.begin();
245 it != sync_changes.end(); ++it) {
246 SettingSyncData data(*it);
247 grouped_sync_data[data.extension_id()].push_back(data);
250 // Create any storage areas that don't exist yet but have sync data.
251 base::DictionaryValue empty;
252 for (std::map<std::string, SettingSyncDataList>::iterator
253 it = grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) {
254 SyncableSettingsStorage* storage =
255 GetOrCreateStorageWithSyncData(it->first, empty);
256 syncer::SyncError error = storage->ProcessSyncChanges(it->second);
258 storage->StopSyncing();
261 return syncer::SyncError();
264 void SettingsBackend::StopSyncing(syncer::ModelType type) {
265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
266 DCHECK(type == syncer::EXTENSION_SETTINGS ||
267 type == syncer::APP_SETTINGS);
268 DCHECK_EQ(sync_type_, type);
270 for (StorageObjMap::iterator it = storage_objs_.begin();
271 it != storage_objs_.end(); ++it) {
272 // Some storage areas may have already stopped syncing if they had areas
273 // and syncing was disabled, but StopSyncing is safe to call multiple times.
274 it->second->StopSyncing();
277 sync_processor_.reset();
278 sync_error_factory_.reset();
281 scoped_ptr<SettingsSyncProcessor> SettingsBackend::CreateSettingsSyncProcessor(
282 const std::string& extension_id) const {
283 CHECK(sync_processor_.get());
284 return scoped_ptr<SettingsSyncProcessor>(
285 new SettingsSyncProcessor(extension_id,
287 sync_processor_.get()));
290 } // namespace extensions