- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / storage / settings_backend.cc
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.
4
5 #include "chrome/browser/extensions/api/storage/settings_backend.h"
6
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"
14
15 using content::BrowserThread;
16
17 namespace extensions {
18
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),
28       quota_(quota),
29       observers_(observers),
30       sync_type_(sync_type),
31       flare_(flare) {
32   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
33   DCHECK(sync_type_ == syncer::EXTENSION_SETTINGS ||
34          sync_type_ == syncer::APP_SETTINGS);
35 }
36
37 SettingsBackend::~SettingsBackend() {
38   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
39 }
40
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);
46 }
47
48 SyncableSettingsStorage* SettingsBackend::GetOrCreateStorageWithSyncData(
49     const std::string& extension_id,
50     const base::DictionaryValue& sync_data) const {
51   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
52
53   StorageObjMap::iterator maybe_storage = storage_objs_.find(extension_id);
54   if (maybe_storage != storage_objs_.end()) {
55     return maybe_storage->second.get();
56   }
57
58   ValueStore* storage = storage_factory_->Create(base_path_, extension_id);
59   CHECK(storage);
60
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);
64
65   linked_ptr<SyncableSettingsStorage> syncable_storage(
66       new SyncableSettingsStorage(
67           observers_,
68           extension_id,
69           storage));
70   storage_objs_[extension_id] = syncable_storage;
71
72   if (sync_processor_.get()) {
73     syncer::SyncError error =
74         syncable_storage->StartSyncing(
75             sync_data,
76             CreateSettingsSyncProcessor(extension_id).Pass());
77     if (error.IsSet())
78       syncable_storage.get()->StopSyncing();
79   } else {
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_);
84   }
85
86   return syncable_storage.get();
87 }
88
89 void SettingsBackend::DeleteStorage(const std::string& extension_id) {
90   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
91
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_|.
95   //
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())
102     return;
103   maybe_storage->second->Clear();
104   storage_objs_.erase(extension_id);
105 }
106
107 std::set<std::string> SettingsBackend::GetKnownExtensionIDs() const {
108   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
109   std::set<std::string> result;
110
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);
116   }
117
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);
128     }
129   }
130
131   return result;
132 }
133
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));
142   }
143 }
144
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);
152
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());
157
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;
164       continue;
165     }
166     AddAllSyncData(*it, maybe_settings->settings(), type, &all_sync_data);
167   }
168
169   return all_sync_data;
170 }
171
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());
182
183   sync_processor_ = sync_processor.Pass();
184   sync_error_factory_ = sync_error_factory.Pass();
185
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;
197     }
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());
201   }
202
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);
215     } else {
216       base::DictionaryValue empty;
217       error = it->second->StartSyncing(
218           empty,
219           CreateSettingsSyncProcessor(it->first).Pass());
220     }
221     if (error.IsSet())
222       it->second->StopSyncing();
223   }
224
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
227   // them.
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);
231   }
232
233   return syncer::SyncMergeResult(type);
234 }
235
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());
241
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);
248   }
249
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);
257     if (error.IsSet())
258       storage->StopSyncing();
259   }
260
261   return syncer::SyncError();
262 }
263
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);
269
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();
275   }
276
277   sync_processor_.reset();
278   sync_error_factory_.reset();
279 }
280
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,
286                                 sync_type_,
287                                 sync_processor_.get()));
288 }
289
290 }  // namespace extensions