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.
4
5#include "chrome/browser/extensions/api/storage/sync_storage_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
19namespace {
20
21void 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));
28  }
29}
30
31}  // namespace
32
33SyncStorageBackend::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),
42      quota_(quota),
43      observers_(observers),
44      sync_type_(sync_type),
45      flare_(flare) {
46  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
47  DCHECK(sync_type_ == syncer::EXTENSION_SETTINGS ||
48         sync_type_ == syncer::APP_SETTINGS);
49}
50
51SyncStorageBackend::~SyncStorageBackend() {}
52
53ValueStore* SyncStorageBackend::GetStorage(const std::string& extension_id) {
54  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
55  base::DictionaryValue empty;
56  return GetOrCreateStorageWithSyncData(extension_id, empty);
57}
58
59SyncableSettingsStorage* SyncStorageBackend::GetOrCreateStorageWithSyncData(
60    const std::string& extension_id,
61    const base::DictionaryValue& sync_data) const {
62  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
63
64  StorageObjMap::iterator maybe_storage = storage_objs_.find(extension_id);
65  if (maybe_storage != storage_objs_.end()) {
66    return maybe_storage->second.get();
67  }
68
69  scoped_ptr<SettingsStorageQuotaEnforcer> storage(
70      new SettingsStorageQuotaEnforcer(
71          quota_, storage_factory_->Create(base_path_, extension_id)));
72
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;
79
80  if (sync_processor_.get()) {
81    syncer::SyncError error = syncable_storage->StartSyncing(
82        sync_data, CreateSettingsSyncProcessor(extension_id).Pass());
83    if (error.IsSet())
84      syncable_storage.get()->StopSyncing();
85  }
86  return syncable_storage.get();
87}
88
89void SyncStorageBackend::DeleteStorage(const std::string& extension_id) {
90  DCHECK_CURRENTLY_ON(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> SyncStorageBackend::GetKnownExtensionIDs() const {
108  DCHECK_CURRENTLY_ON(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();
115       ++it) {
116    result.insert(it->first);
117  }
118
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);
129    }
130  }
131
132  return result;
133}
134
135syncer::SyncDataList SyncStorageBackend::GetAllSyncData(syncer::ModelType type)
136    const {
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);
141
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());
146
147  for (std::set<std::string>::const_iterator it = known_extension_ids.begin();
148       it != known_extension_ids.end();
149       ++it) {
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;
155      continue;
156    }
157    AddAllSyncData(*it, maybe_settings->settings(), type, &all_sync_data);
158  }
159
160  return all_sync_data;
161}
162
163syncer::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());
173
174  sync_processor_ = sync_processor.Pass();
175  sync_error_factory_ = sync_error_factory.Pass();
176
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();
181       ++it) {
182    SettingSyncData data(*it);
183    linked_ptr<base::DictionaryValue> sync_data =
184        grouped_sync_data[data.extension_id()];
185    if (!sync_data.get()) {
186      sync_data =
187          linked_ptr<base::DictionaryValue>(new base::DictionaryValue());
188      grouped_sync_data[data.extension_id()] = sync_data;
189    }
190    DCHECK(!sync_data->HasKey(data.key())) << "Duplicate settings for "
191                                           << data.extension_id() << "/"
192                                           << data.key();
193    sync_data->SetWithoutPathExpansion(data.key(), data.value().DeepCopy());
194  }
195
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();
200       ++it) {
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);
209    } else {
210      base::DictionaryValue empty;
211      error = it->second->StartSyncing(
212          empty, CreateSettingsSyncProcessor(it->first).Pass());
213    }
214    if (error.IsSet())
215      it->second->StopSyncing();
216  }
217
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
220  // them.
221  for (std::map<std::string, linked_ptr<base::DictionaryValue> >::iterator it =
222           grouped_sync_data.begin();
223       it != grouped_sync_data.end();
224       ++it) {
225    GetOrCreateStorageWithSyncData(it->first, *it->second);
226  }
227
228  return syncer::SyncMergeResult(type);
229}
230
231syncer::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());
236
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();
241       ++it) {
242    SettingSyncData data(*it);
243    grouped_sync_data[data.extension_id()].push_back(data);
244  }
245
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();
251       ++it) {
252    SyncableSettingsStorage* storage =
253        GetOrCreateStorageWithSyncData(it->first, empty);
254    syncer::SyncError error = storage->ProcessSyncChanges(it->second);
255    if (error.IsSet())
256      storage->StopSyncing();
257  }
258
259  return syncer::SyncError();
260}
261
262void 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);
266
267  for (StorageObjMap::iterator it = storage_objs_.begin();
268       it != storage_objs_.end();
269       ++it) {
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();
273  }
274
275  sync_processor_.reset();
276  sync_error_factory_.reset();
277}
278
279scoped_ptr<SettingsSyncProcessor>
280SyncStorageBackend::CreateSettingsSyncProcessor(const std::string& extension_id)
281    const {
282  CHECK(sync_processor_.get());
283  return scoped_ptr<SettingsSyncProcessor>(new SettingsSyncProcessor(
284      extension_id, sync_type_, sync_processor_.get()));
285}
286
287}  // namespace extensions
288