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
15using content::BrowserThread;
16
17namespace extensions {
18
19SettingsBackend::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
37SettingsBackend::~SettingsBackend() {
38  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
39}
40
41ValueStore* 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
48SyncableSettingsStorage* 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
89void 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
107std::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
134static 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
145syncer::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();
164      continue;
165    }
166    AddAllSyncData(*it, *maybe_settings->settings().get(),
167                   type, &all_sync_data);
168  }
169
170  return all_sync_data;
171}
172
173syncer::SyncMergeResult SettingsBackend::MergeDataAndStartSyncing(
174    syncer::ModelType type,
175    const syncer::SyncDataList& initial_sync_data,
176    scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
177    scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) {
178  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
179  DCHECK_EQ(sync_type_, type);
180  DCHECK(!sync_processor_.get());
181  DCHECK(sync_processor.get());
182  DCHECK(sync_error_factory.get());
183
184  sync_processor_ = sync_processor.Pass();
185  sync_error_factory_ = sync_error_factory.Pass();
186
187  // Group the initial sync data by extension id.
188  std::map<std::string, linked_ptr<base::DictionaryValue> > grouped_sync_data;
189  for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin();
190      it != initial_sync_data.end(); ++it) {
191    SettingSyncData data(*it);
192    linked_ptr<base::DictionaryValue> sync_data =
193        grouped_sync_data[data.extension_id()];
194    if (!sync_data.get()) {
195      sync_data = linked_ptr<base::DictionaryValue>(
196          new base::DictionaryValue());
197      grouped_sync_data[data.extension_id()] = sync_data;
198    }
199    DCHECK(!sync_data->HasKey(data.key())) <<
200        "Duplicate settings for " << data.extension_id() << "/" << data.key();
201    sync_data->SetWithoutPathExpansion(data.key(), data.value().DeepCopy());
202  }
203
204  // Start syncing all existing storage areas.  Any storage areas created in
205  // the future will start being synced as part of the creation process.
206  for (StorageObjMap::iterator it = storage_objs_.begin();
207      it != storage_objs_.end(); ++it) {
208    std::map<std::string, linked_ptr<base::DictionaryValue> >::iterator
209        maybe_sync_data = grouped_sync_data.find(it->first);
210    syncer::SyncError error;
211    if (maybe_sync_data != grouped_sync_data.end()) {
212      error = it->second->StartSyncing(
213          *maybe_sync_data->second,
214          CreateSettingsSyncProcessor(it->first).Pass());
215      grouped_sync_data.erase(it->first);
216    } else {
217      base::DictionaryValue empty;
218      error = it->second->StartSyncing(
219          empty,
220          CreateSettingsSyncProcessor(it->first).Pass());
221    }
222    if (error.IsSet())
223      it->second->StopSyncing();
224  }
225
226  // Eagerly create and init the rest of the storage areas that have sync data.
227  // Under normal circumstances (i.e. not first-time sync) this will be all of
228  // them.
229  for (std::map<std::string, linked_ptr<base::DictionaryValue> >::iterator it =
230      grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) {
231    GetOrCreateStorageWithSyncData(it->first, *it->second);
232  }
233
234  return syncer::SyncMergeResult(type);
235}
236
237syncer::SyncError SettingsBackend::ProcessSyncChanges(
238    const tracked_objects::Location& from_here,
239    const syncer::SyncChangeList& sync_changes) {
240  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
241  DCHECK(sync_processor_.get());
242
243  // Group changes by extension, to pass all changes in a single method call.
244  std::map<std::string, SettingSyncDataList> grouped_sync_data;
245  for (syncer::SyncChangeList::const_iterator it = sync_changes.begin();
246      it != sync_changes.end(); ++it) {
247    SettingSyncData data(*it);
248    grouped_sync_data[data.extension_id()].push_back(data);
249  }
250
251  // Create any storage areas that don't exist yet but have sync data.
252  base::DictionaryValue empty;
253  for (std::map<std::string, SettingSyncDataList>::iterator
254      it = grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) {
255    SyncableSettingsStorage* storage =
256        GetOrCreateStorageWithSyncData(it->first, empty);
257    syncer::SyncError error = storage->ProcessSyncChanges(it->second);
258    if (error.IsSet())
259      storage->StopSyncing();
260  }
261
262  return syncer::SyncError();
263}
264
265void SettingsBackend::StopSyncing(syncer::ModelType type) {
266  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
267  DCHECK(type == syncer::EXTENSION_SETTINGS ||
268         type == syncer::APP_SETTINGS);
269  DCHECK_EQ(sync_type_, type);
270
271  for (StorageObjMap::iterator it = storage_objs_.begin();
272      it != storage_objs_.end(); ++it) {
273    // Some storage areas may have already stopped syncing if they had areas
274    // and syncing was disabled, but StopSyncing is safe to call multiple times.
275    it->second->StopSyncing();
276  }
277
278  sync_processor_.reset();
279  sync_error_factory_.reset();
280}
281
282scoped_ptr<SettingsSyncProcessor> SettingsBackend::CreateSettingsSyncProcessor(
283    const std::string& extension_id) const {
284  CHECK(sync_processor_.get());
285  return scoped_ptr<SettingsSyncProcessor>(
286      new SettingsSyncProcessor(extension_id,
287                                sync_type_,
288                                sync_processor_.get()));
289}
290
291}  // namespace extensions
292