summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-21 19:07:13 +0000
committerakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-21 19:07:13 +0000
commitadfc54690d913542f0cdf0f4ea1339ba1670df24 (patch)
tree4f20cda44225f5384193bba9933e3676bff6047c
parent02896a8fd669ced03e8a76edfbb423c858eafc53 (diff)
downloadchromium_src-adfc54690d913542f0cdf0f4ea1339ba1670df24.zip
chromium_src-adfc54690d913542f0cdf0f4ea1339ba1670df24.tar.gz
chromium_src-adfc54690d913542f0cdf0f4ea1339ba1670df24.tar.bz2
Enable sync for the settings from the Extension Settings API.
Original patch by kalman@chromium.org. BUG=94575 TEST=unit tests included TBR=kalman@chromium.org Review URL: http://codereview.chromium.org/7977018 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@102140 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension_service.cc8
-rw-r--r--chrome/browser/extensions/extension_service.h9
-rw-r--r--chrome/browser/extensions/extension_setting_sync_data.cc78
-rw-r--r--chrome/browser/extensions/extension_setting_sync_data.h77
-rw-r--r--chrome/browser/extensions/extension_settings.cc269
-rw-r--r--chrome/browser/extensions/extension_settings.h68
-rw-r--r--chrome/browser/extensions/extension_settings_api.cc11
-rw-r--r--chrome/browser/extensions/extension_settings_api.h5
-rw-r--r--chrome/browser/extensions/extension_settings_cached_leveldb_storage_unittest.cc4
-rw-r--r--chrome/browser/extensions/extension_settings_cached_noop_storage_unittest.cc4
-rw-r--r--chrome/browser/extensions/extension_settings_leveldb_storage_unittest.cc5
-rw-r--r--chrome/browser/extensions/extension_settings_storage.h2
-rw-r--r--chrome/browser/extensions/extension_settings_storage_unittest.cc25
-rw-r--r--chrome/browser/extensions/extension_settings_storage_unittest.h13
-rw-r--r--chrome/browser/extensions/extension_settings_sync_unittest.cc492
-rw-r--r--chrome/browser/extensions/extension_settings_sync_util.cc51
-rw-r--r--chrome/browser/extensions/extension_settings_sync_util.h43
-rw-r--r--chrome/browser/extensions/extension_settings_ui_wrapper.cc65
-rw-r--r--chrome/browser/extensions/extension_settings_ui_wrapper.h63
-rw-r--r--chrome/browser/extensions/syncable_extension_settings_storage.cc341
-rw-r--r--chrome/browser/extensions/syncable_extension_settings_storage.h79
-rw-r--r--chrome/browser/profiles/profile_impl.cc4
-rw-r--r--chrome/browser/profiles/profile_impl.h1
-rw-r--r--chrome/browser/sync/glue/data_type_manager_impl.cc1
-rw-r--r--chrome/browser/sync/glue/extension_setting_data_type_controller.cc107
-rw-r--r--chrome/browser/sync/glue/extension_setting_data_type_controller.h64
-rw-r--r--chrome/browser/sync/profile_sync_factory.h9
-rw-r--r--chrome/browser/sync/profile_sync_factory_impl.cc27
-rw-r--r--chrome/browser/sync/profile_sync_factory_impl.h6
-rw-r--r--chrome/browser/sync/profile_sync_factory_mock.h6
-rw-r--r--chrome/browser/sync/profile_sync_service.cc5
-rw-r--r--chrome/browser/sync/protocol/extension_setting_specifics.proto33
-rw-r--r--chrome/browser/sync/protocol/nigori_specifics.proto2
-rw-r--r--chrome/browser/sync/protocol/proto_value_conversions.cc12
-rw-r--r--chrome/browser/sync/protocol/proto_value_conversions.h4
-rw-r--r--chrome/browser/sync/protocol/proto_value_conversions_unittest.cc8
-rw-r--r--chrome/browser/sync/protocol/sync_proto.gyp1
-rw-r--r--chrome/browser/sync/syncable/model_type.cc27
-rw-r--r--chrome/browser/sync/syncable/model_type.h2
-rw-r--r--chrome/browser/sync/util/cryptographer.cc4
-rw-r--r--chrome/browser/ui/cocoa/extensions/extension_popup_controller_unittest.mm5
-rw-r--r--chrome/chrome_browser.gypi10
-rw-r--r--chrome/chrome_tests.gypi13
-rw-r--r--chrome/common/chrome_switches.cc4
-rw-r--r--chrome/common/chrome_switches.h3
-rw-r--r--chrome/common/pref_names.cc1
-rw-r--r--chrome/common/pref_names.h1
-rw-r--r--chrome/test/base/testing_profile.cc3
-rw-r--r--chrome/test/base/testing_profile.h5
-rwxr-xr-xnet/tools/testserver/chromiumsync.py8
50 files changed, 1967 insertions, 121 deletions
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index bf77408..d18e004 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -581,14 +581,14 @@ ExtensionService::ExtensionService(Profile* profile,
const CommandLine* command_line,
const FilePath& install_directory,
ExtensionPrefs* extension_prefs,
- ExtensionSettings* extension_settings,
bool autoupdate_enabled,
bool extensions_enabled)
: weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
profile_(profile),
extension_prefs_(extension_prefs),
- extension_settings_(extension_settings),
+ extension_settings_(
+ profile->GetPath().AppendASCII(kSettingsDirectoryName)),
pending_extension_manager_(*ALLOW_THIS_IN_INITIALIZER_LIST(this)),
install_directory_(install_directory),
extensions_enabled_(extensions_enabled),
@@ -1664,8 +1664,8 @@ ExtensionPrefs* ExtensionService::extension_prefs() {
return extension_prefs_;
}
-ExtensionSettings* ExtensionService::extension_settings() {
- return extension_settings_;
+ExtensionSettingsUIWrapper* ExtensionService::extension_settings() {
+ return &extension_settings_;
}
ExtensionContentSettingsStore*
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 51e2d91..ffa598b 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -28,6 +28,7 @@
#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/browser/extensions/extension_permissions_api.h"
#include "chrome/browser/extensions/extension_process_manager.h"
+#include "chrome/browser/extensions/extension_settings_ui_wrapper.h"
#include "chrome/browser/extensions/extension_sync_data.h"
#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/extensions/extensions_quota_service.h"
@@ -56,7 +57,6 @@ class ExtensionInstallUI;
class ExtensionManagementEventRouter;
class ExtensionPreferenceEventRouter;
class ExtensionServiceBackend;
-class ExtensionSettings;
class ExtensionSyncData;
class ExtensionToolbarModel;
class ExtensionUpdater;
@@ -179,7 +179,6 @@ class ExtensionService
const CommandLine* command_line,
const FilePath& install_directory,
ExtensionPrefs* extension_prefs,
- ExtensionSettings* extension_settings,
bool autoupdate_enabled,
bool extensions_enabled);
@@ -440,7 +439,7 @@ class ExtensionService
// ExtensionPrefs* mutable_extension_prefs().
ExtensionPrefs* extension_prefs();
- ExtensionSettings* extension_settings();
+ ExtensionSettingsUIWrapper* extension_settings();
ExtensionContentSettingsStore* GetExtensionContentSettingsStore();
@@ -669,8 +668,8 @@ class ExtensionService
// Preferences for the owning profile (weak reference).
ExtensionPrefs* extension_prefs_;
- // Settings for the owning profile (weak reference).
- ExtensionSettings* extension_settings_;
+ // Settings for the owning profile.
+ ExtensionSettingsUIWrapper extension_settings_;
// The current list of installed extensions.
// TODO(aa): This should use chrome/common/extensions/extension_set.h.
diff --git a/chrome/browser/extensions/extension_setting_sync_data.cc b/chrome/browser/extensions/extension_setting_sync_data.cc
new file mode 100644
index 0000000..96be9a1
--- /dev/null
+++ b/chrome/browser/extensions/extension_setting_sync_data.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/extension_setting_sync_data.h"
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "chrome/browser/sync/api/sync_data.h"
+#include "chrome/browser/sync/protocol/extension_setting_specifics.pb.h"
+
+ExtensionSettingSyncData::ExtensionSettingSyncData(
+ const SyncChange& sync_change) {
+ Init(sync_change.change_type(), sync_change.sync_data());
+}
+
+ExtensionSettingSyncData::ExtensionSettingSyncData(
+ const SyncData& sync_data) {
+ Init(SyncChange::ACTION_INVALID, sync_data);
+}
+
+void ExtensionSettingSyncData::Init(
+ SyncChange::SyncChangeType change_type, const SyncData& sync_data) {
+ DCHECK(!internal_.get());
+ sync_pb::ExtensionSettingSpecifics specifics =
+ sync_data.GetSpecifics().GetExtension(sync_pb::extension_setting);
+ Value* value =
+ base::JSONReader().JsonToValue(specifics.value(), false, false);
+ if (!value) {
+ LOG(WARNING) << "Specifics for " << specifics.extension_id() << "/" <<
+ specifics.key() << " had bad JSON for value: " << specifics.value();
+ value = new DictionaryValue();
+ }
+ internal_ = new Internal(
+ change_type,
+ specifics.extension_id(),
+ specifics.key(),
+ value);
+}
+
+ExtensionSettingSyncData::ExtensionSettingSyncData(
+ SyncChange::SyncChangeType change_type,
+ const std::string& extension_id,
+ const std::string& key,
+ Value* value)
+ : internal_(new Internal(change_type, extension_id, key, value)) {
+ CHECK(value);
+}
+
+ExtensionSettingSyncData::~ExtensionSettingSyncData() {}
+
+SyncChange::SyncChangeType ExtensionSettingSyncData::change_type() const {
+ return internal_->change_type_;
+}
+
+const std::string& ExtensionSettingSyncData::extension_id() const {
+ return internal_->extension_id_;
+}
+
+const std::string& ExtensionSettingSyncData::key() const {
+ return internal_->key_;
+}
+
+const Value& ExtensionSettingSyncData::value() const {
+ return *internal_->value_;
+}
+
+ExtensionSettingSyncData::Internal::Internal(
+ SyncChange::SyncChangeType change_type,
+ const std::string& extension_id,
+ const std::string& key,
+ Value* value)
+ : change_type_(change_type),
+ extension_id_(extension_id),
+ key_(key),
+ value_(value) {}
+
+ExtensionSettingSyncData::Internal::~Internal() {}
diff --git a/chrome/browser/extensions/extension_setting_sync_data.h b/chrome/browser/extensions/extension_setting_sync_data.h
new file mode 100644
index 0000000..48d6d6f
--- /dev/null
+++ b/chrome/browser/extensions/extension_setting_sync_data.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTING_SYNC_DATA_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTING_SYNC_DATA_H_
+#pragma once
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/sync/api/sync_change.h"
+
+class SyncData;
+
+// Container for data interpreted from sync data/changes. Safe and efficient
+// to copy.
+class ExtensionSettingSyncData {
+ public:
+ // Creates from a sync change.
+ explicit ExtensionSettingSyncData(const SyncChange& sync_change);
+
+ // Creates from sync data. change_type will be ACTION_INVALID.
+ explicit ExtensionSettingSyncData(const SyncData& sync_data);
+
+ // Creates explicitly.
+ ExtensionSettingSyncData(
+ SyncChange::SyncChangeType change_type,
+ const std::string& extension_id,
+ const std::string& key,
+ // May NOT be NULL. Ownership taken.
+ Value* value);
+
+ ~ExtensionSettingSyncData();
+
+ // Returns the type of the sync change; may be ACTION_INVALID.
+ SyncChange::SyncChangeType change_type() const;
+
+ // Returns the extension id the setting is for.
+ const std::string& extension_id() const;
+
+ // Returns the settings key.
+ const std::string& key() const;
+
+ // Returns the value of the setting.
+ const Value& value() const;
+
+ private:
+ // Ref-counted container for the data.
+ // TODO(kalman): Use browser_sync::Immutable<Internal>.
+ class Internal : public base::RefCountedThreadSafe<Internal> {
+ public:
+ explicit Internal(
+ SyncChange::SyncChangeType change_type,
+ const std::string& extension_id,
+ const std::string& key,
+ Value* value);
+
+ SyncChange::SyncChangeType change_type_;
+ std::string extension_id_;
+ std::string key_;
+ scoped_ptr<Value> value_;
+
+ private:
+ friend class base::RefCountedThreadSafe<Internal>;
+ ~Internal();
+ };
+
+ // Initializes internal_ from sync data.
+ void Init(SyncChange::SyncChangeType change_type, const SyncData& sync_data);
+
+ scoped_refptr<Internal> internal_;
+};
+
+typedef std::vector<ExtensionSettingSyncData> ExtensionSettingSyncDataList;
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTING_SYNC_DATA_H_
diff --git a/chrome/browser/extensions/extension_settings.cc b/chrome/browser/extensions/extension_settings.cc
index 0d855b95..9349ad06 100644
--- a/chrome/browser/extensions/extension_settings.cc
+++ b/chrome/browser/extensions/extension_settings.cc
@@ -4,76 +4,95 @@
#include "chrome/browser/extensions/extension_settings.h"
-#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/file_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
+#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
-#include "content/browser/browser_thread.h"
#include "chrome/browser/extensions/extension_settings_leveldb_storage.h"
#include "chrome/browser/extensions/extension_settings_noop_storage.h"
#include "chrome/browser/extensions/extension_settings_storage_cache.h"
+#include "chrome/browser/extensions/extension_settings_sync_util.h"
+#include "chrome/common/extensions/extension.h"
+#include "content/browser/browser_thread.h"
#include "third_party/leveldatabase/src/include/leveldb/iterator.h"
#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
ExtensionSettings::ExtensionSettings(const FilePath& base_path)
- : base_path_(base_path) {}
+ : base_path_(base_path),
+ sync_processor_(NULL) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+}
ExtensionSettings::~ExtensionSettings() {
- std::map<std::string, ExtensionSettingsStorage*>::iterator it;
- for (it = storage_objs_.begin(); it != storage_objs_.end(); ++it) {
- BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, it->second);
- }
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
}
ExtensionSettingsStorage* ExtensionSettings::GetStorage(
- const std::string& extension_id) {
+ const std::string& extension_id) const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DictionaryValue empty;
+ return GetOrCreateStorageWithSyncData(extension_id, empty);
+}
- std::map<std::string, ExtensionSettingsStorage*>::iterator existing =
- storage_objs_.find(extension_id);
- if (existing != storage_objs_.end()) {
- return existing->second;
- }
-
- ExtensionSettingsStorage* new_storage =
- CreateStorage(extension_id, ExtensionSettingsStorage::LEVELDB, true);
- if (new_storage == NULL) {
- // Failed to create a leveldb storage for some reason. Use an in memory
- // storage area (no-op wrapped in a cache) instead.
- new_storage =
- CreateStorage(extension_id, ExtensionSettingsStorage::NOOP, true);
- DCHECK(new_storage != NULL);
+SyncableExtensionSettingsStorage*
+ExtensionSettings::GetOrCreateStorageWithSyncData(
+ const std::string& extension_id, const DictionaryValue& sync_data) const {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ SyncableExtensionSettingsStorage* storage = GetOrCreateAndInitStorage(
+ ExtensionSettingsStorage::LEVELDB,
+ true,
+ extension_id,
+ sync_data);
+ if (!storage) {
+ // Fall back to an in-memory storage area (cached NOOP).
+ // It's ok for these to be synced, it just means that on next starting up
+ // extensions will see the "old" settings, then overwritten (and notified)
+ // when the sync changes come through.
+ storage = GetOrCreateAndInitStorage(
+ ExtensionSettingsStorage::NOOP,
+ true,
+ extension_id,
+ sync_data);
+ DCHECK(storage);
}
-
- storage_objs_[extension_id] = new_storage;
- return new_storage;
+ return storage;
}
ExtensionSettingsStorage* ExtensionSettings::GetStorageForTesting(
ExtensionSettingsStorage::Type type,
bool cached,
- const std::string& extension_id) {
+ const std::string& extension_id) const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DictionaryValue empty;
+ ExtensionSettingsStorage* storage =
+ GetOrCreateAndInitStorage(type, cached, extension_id, empty);
+ DCHECK(storage);
+ return storage;
+}
- std::map<std::string, ExtensionSettingsStorage*>::iterator existing =
- storage_objs_.find(extension_id);
+SyncableExtensionSettingsStorage* ExtensionSettings::GetOrCreateAndInitStorage(
+ ExtensionSettingsStorage::Type type,
+ bool cached,
+ const std::string& extension_id,
+ const DictionaryValue& initial_sync_data) const {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ StorageObjMap::iterator existing = storage_objs_.find(extension_id);
if (existing != storage_objs_.end()) {
- return existing->second;
+ return existing->second.get();
}
-
- ExtensionSettingsStorage* new_storage =
- CreateStorage(extension_id, type, cached);
- DCHECK(new_storage != NULL);
- storage_objs_[extension_id] = new_storage;
- return new_storage;
+ return CreateAndInitStorage(type, cached, extension_id, initial_sync_data);
}
-ExtensionSettingsStorage* ExtensionSettings::CreateStorage(
- const std::string& extension_id,
+SyncableExtensionSettingsStorage* ExtensionSettings::CreateAndInitStorage(
ExtensionSettingsStorage::Type type,
- bool cached) {
+ bool cached,
+ const std::string& extension_id,
+ const DictionaryValue& initial_sync_data) const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_EQ(0u, storage_objs_.count(extension_id));
ExtensionSettingsStorage* storage = NULL;
switch (type) {
case ExtensionSettingsStorage::NOOP:
@@ -86,8 +105,178 @@ ExtensionSettingsStorage* ExtensionSettings::CreateStorage(
default:
NOTREACHED();
}
- if (storage != NULL && cached) {
+ if (!storage) {
+ return NULL;
+ }
+ if (cached) {
storage = new ExtensionSettingsStorageCache(storage);
}
- return storage;
+
+ SyncableExtensionSettingsStorage* synced_storage =
+ new SyncableExtensionSettingsStorage(extension_id, storage);
+ if (sync_processor_) {
+ // TODO(kalman): do something if StartSyncing fails.
+ ignore_result(
+ synced_storage->StartSyncing(initial_sync_data, sync_processor_));
+ }
+ storage_objs_[extension_id] =
+ linked_ptr<SyncableExtensionSettingsStorage>(synced_storage);
+ return synced_storage;
+}
+
+std::set<std::string> ExtensionSettings::GetKnownExtensionIDs() const {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ std::set<std::string> result;
+
+ // TODO(kalman): we will need to do something to disambiguate between app
+ // settings and extension settings, since settings for apps should be synced
+ // iff app sync is turned on, ditto for extensions.
+
+ // Extension IDs live in-memory and/or on disk. The cache will contain all
+ // that are in-memory.
+ for (StorageObjMap::iterator it = storage_objs_.begin();
+ it != storage_objs_.end(); ++it) {
+ result.insert(it->first);
+ }
+
+ // Leveldb databases are directories inside base_path_.
+ file_util::FileEnumerator::FindInfo find_info;
+ file_util::FileEnumerator extension_dirs(
+ base_path_, false, file_util::FileEnumerator::DIRECTORIES);
+ while (!extension_dirs.Next().empty()) {
+ extension_dirs.GetFindInfo(&find_info);
+ FilePath extension_dir(file_util::FileEnumerator::GetFilename(find_info));
+ DCHECK(!extension_dir.IsAbsolute());
+ // Extension IDs are created as std::strings so they *should* be ASCII.
+ std::string maybe_as_ascii(extension_dir.MaybeAsASCII());
+ if (!maybe_as_ascii.empty()) {
+ result.insert(maybe_as_ascii);
+ }
+ }
+
+ return result;
+}
+
+SyncDataList ExtensionSettings::GetAllSyncData(
+ syncable::ModelType type) const {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_EQ(type, syncable::EXTENSION_SETTINGS);
+
+ // For all extensions, get all their settings.
+ // This has the effect of bringing in the entire state of extension settings
+ // in memory; sad.
+ SyncDataList all_sync_data;
+ std::set<std::string> known_extension_ids(GetKnownExtensionIDs());
+
+ for (std::set<std::string>::const_iterator it = known_extension_ids.begin();
+ it != known_extension_ids.end(); ++it) {
+ ExtensionSettingsStorage::Result maybe_settings = GetStorage(*it)->Get();
+ if (maybe_settings.HasError()) {
+ LOG(WARNING) << "Failed to get settings for " << *it << ": " <<
+ maybe_settings.GetError();
+ continue;
+ }
+
+ DictionaryValue* settings = maybe_settings.GetSettings();
+ for (DictionaryValue::key_iterator key_it = settings->begin_keys();
+ key_it != settings->end_keys(); ++key_it) {
+ Value *value = NULL;
+ settings->GetWithoutPathExpansion(*key_it, &value);
+ all_sync_data.push_back(
+ extension_settings_sync_util::CreateData(*it, *key_it, *value));
+ }
+ }
+
+ return all_sync_data;
+}
+
+SyncError ExtensionSettings::MergeDataAndStartSyncing(
+ syncable::ModelType type,
+ const SyncDataList& initial_sync_data,
+ SyncChangeProcessor* sync_processor) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_EQ(type, syncable::EXTENSION_SETTINGS);
+ DCHECK(!sync_processor_);
+ sync_processor_ = sync_processor;
+
+ // Group the initial sync data by extension id.
+ std::map<std::string, linked_ptr<DictionaryValue> > grouped_sync_data;
+ for (SyncDataList::const_iterator it = initial_sync_data.begin();
+ it != initial_sync_data.end(); ++it) {
+ ExtensionSettingSyncData data(*it);
+ linked_ptr<DictionaryValue> sync_data =
+ grouped_sync_data[data.extension_id()];
+ if (!sync_data.get()) {
+ sync_data = linked_ptr<DictionaryValue>(new DictionaryValue());
+ grouped_sync_data[data.extension_id()] = sync_data;
+ }
+ DCHECK(!sync_data->HasKey(data.key())) <<
+ "Duplicate settings for " << data.extension_id() << "/" << data.key();
+ sync_data->Set(data.key(), data.value().DeepCopy());
+ }
+
+ // Start syncing all existing storage areas. Any storage areas created in
+ // the future will start being synced as part of the creation process.
+ for (StorageObjMap::iterator it = storage_objs_.begin();
+ it != storage_objs_.end(); ++it) {
+ std::map<std::string, linked_ptr<DictionaryValue> >::iterator
+ maybe_sync_data = grouped_sync_data.find(it->first);
+ if (maybe_sync_data != grouped_sync_data.end()) {
+ // TODO(kalman): do something if StartSyncing fails.
+ ignore_result(
+ it->second->StartSyncing(*maybe_sync_data->second, sync_processor));
+ grouped_sync_data.erase(it->first);
+ } else {
+ DictionaryValue empty;
+ // TODO(kalman): do something if StartSyncing fails.
+ ignore_result(it->second->StartSyncing(empty, sync_processor));
+ }
+ }
+
+ // Eagerly create and init the rest of the storage areas that have sync data.
+ // Under normal circumstances (i.e. not first-time sync) this will be all of
+ // them.
+ for (std::map<std::string, linked_ptr<DictionaryValue> >::iterator it =
+ grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) {
+ GetOrCreateStorageWithSyncData(it->first, *it->second);
+ }
+
+ return SyncError();
+}
+
+SyncError ExtensionSettings::ProcessSyncChanges(
+ const tracked_objects::Location& from_here,
+ const SyncChangeList& sync_changes) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(sync_processor_);
+
+ // Group changes by extension, to pass all changes in a single method call.
+ std::map<std::string, ExtensionSettingSyncDataList> grouped_sync_data;
+ for (SyncChangeList::const_iterator it = sync_changes.begin();
+ it != sync_changes.end(); ++it) {
+ ExtensionSettingSyncData data(*it);
+ grouped_sync_data[data.extension_id()].push_back(data);
+ }
+
+ DictionaryValue empty;
+ for (std::map<std::string, ExtensionSettingSyncDataList>::iterator
+ it = grouped_sync_data.begin(); it != grouped_sync_data.end(); ++it) {
+ // TODO(kalman): do something if ProcessSyncChanges fails.
+ ignore_result(
+ GetOrCreateStorageWithSyncData(it->first, empty)->
+ ProcessSyncChanges(it->second));
+ }
+
+ return SyncError();
+}
+
+void ExtensionSettings::StopSyncing(syncable::ModelType type) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(sync_processor_);
+ sync_processor_ = NULL;
+
+ for (StorageObjMap::iterator it = storage_objs_.begin();
+ it != storage_objs_.end(); ++it) {
+ it->second->StopSyncing();
+ }
}
diff --git a/chrome/browser/extensions/extension_settings.h b/chrome/browser/extensions/extension_settings.h
index 1510a53..b6f87d8 100644
--- a/chrome/browser/extensions/extension_settings.h
+++ b/chrome/browser/extensions/extension_settings.h
@@ -6,23 +6,32 @@
#define CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_H_
#pragma once
+#include "base/compiler_specific.h"
#include "base/file_path.h"
+#include "base/memory/linked_ptr.h"
#include "base/memory/ref_counted.h"
-#include "chrome/browser/extensions/extension_settings_storage.h"
+#include "base/task.h"
+#include "chrome/browser/extensions/syncable_extension_settings_storage.h"
+#include "chrome/browser/sync/api/syncable_service.h"
-// Manages ExtensionSettingsStorage objects for extensions.
-class ExtensionSettings : public base::RefCountedThreadSafe<ExtensionSettings> {
+// Manages ExtensionSettingsStorage objects for extensions, including routing
+// changes from sync to them.
+// Lives entirely on the FILE thread.
+class ExtensionSettings : public SyncableService {
public:
// File path is the base of the extension settings directory.
// The databases will be at base_path/extension_id.
explicit ExtensionSettings(const FilePath& base_path);
+ virtual ~ExtensionSettings();
+
// Gets a weak reference to the storage area for a given extension.
// Must be run on the FILE thread.
//
// By default this will be of a cached LEVELDB storage, but on failure to
// create a leveldb instance will fall back to cached NOOP storage.
- ExtensionSettingsStorage* GetStorage(const std::string& extension_id);
+ ExtensionSettingsStorage* GetStorage(
+ const std::string& extension_id) const;
// Gets a weak reference to the storage area for a given extension, with a
// specific type and whether it should be wrapped in a cache.
@@ -32,25 +41,60 @@ class ExtensionSettings : public base::RefCountedThreadSafe<ExtensionSettings> {
ExtensionSettingsStorage* GetStorageForTesting(
ExtensionSettingsStorage::Type type,
bool cached,
- const std::string& extension_id);
+ const std::string& extension_id) const;
+
+ // SyncableService implementation.
+ virtual SyncDataList GetAllSyncData(syncable::ModelType type) const OVERRIDE;
+ virtual SyncError MergeDataAndStartSyncing(
+ syncable::ModelType type,
+ const SyncDataList& initial_sync_data,
+ SyncChangeProcessor* sync_processor) OVERRIDE;
+ virtual SyncError ProcessSyncChanges(
+ const tracked_objects::Location& from_here,
+ const SyncChangeList& change_list) OVERRIDE;
+ virtual void StopSyncing(syncable::ModelType type) OVERRIDE;
private:
- friend class base::RefCountedThreadSafe<ExtensionSettings>;
- virtual ~ExtensionSettings();
+ // Gets a weak reference to the storage area for a given extension,
+ // initializing sync with some initial data if sync enabled.
+ //
+ // By default this will be of a cached LEVELDB storage, but on failure to
+ // create a leveldb instance will fall back to cached NOOP storage.
+ SyncableExtensionSettingsStorage* GetOrCreateStorageWithSyncData(
+ const std::string& extension_id, const DictionaryValue& sync_data) const;
- // Creates a storage area of a given type, optionally wrapped in a cache.
- // Returns NULL if creation fails.
- ExtensionSettingsStorage* CreateStorage(
+ // If a storage area exists in the cache, returns the cached storage area.
+ // Otherwise tries to create one using CreateAndInitStorage.
+ SyncableExtensionSettingsStorage* GetOrCreateAndInitStorage(
+ ExtensionSettingsStorage::Type type,
+ bool cached,
const std::string& extension_id,
+ const DictionaryValue& initial_sync_data) const;
+
+ // Creates a storage area of a given type, optionally wrapped in a cache.
+ // Returns NULL if creation fails. Otherwise, adds the new storage area to
+ // the cache and initializes sync if sync is enabled.
+ SyncableExtensionSettingsStorage* CreateAndInitStorage(
ExtensionSettingsStorage::Type type,
- bool cached);
+ bool cached,
+ const std::string& extension_id,
+ const DictionaryValue& initial_sync_data) const;
+
+ // Gets all extension IDs known to extension settings. This may not be all
+ // installed extensions.
+ std::set<std::string> GetKnownExtensionIDs() const;
// The base file path to create any leveldb databases at.
const FilePath base_path_;
// A cache of ExtensionSettingsStorage objects that have already been created.
// Ensure that there is only ever one created per extension.
- std::map<std::string, ExtensionSettingsStorage*> storage_objs_;
+ typedef std::map<std::string, linked_ptr<SyncableExtensionSettingsStorage> >
+ StorageObjMap;
+ mutable StorageObjMap storage_objs_;
+
+ // Current sync processor, if any.
+ SyncChangeProcessor* sync_processor_;
DISALLOW_COPY_AND_ASSIGN(ExtensionSettings);
};
diff --git a/chrome/browser/extensions/extension_settings_api.cc b/chrome/browser/extensions/extension_settings_api.cc
index 7167901..ad90690 100644
--- a/chrome/browser/extensions/extension_settings_api.cc
+++ b/chrome/browser/extensions/extension_settings_api.cc
@@ -16,17 +16,14 @@ const char* kUnsupportedArgumentType = "Unsupported argument type";
// SettingsFunction
bool SettingsFunction::RunImpl() {
- BrowserThread::PostTask(
- BrowserThread::FILE,
- FROM_HERE,
- base::Bind(&SettingsFunction::RunOnFileThread, this));
+ profile()->GetExtensionService()->extension_settings()->RunWithSettings(
+ base::Bind(&SettingsFunction::RunWithSettingsOnFileThread, this));
return true;
}
-void SettingsFunction::RunOnFileThread() {
+void SettingsFunction::RunWithSettingsOnFileThread(
+ ExtensionSettings* settings) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- ExtensionSettings* settings =
- profile()->GetExtensionService()->extension_settings();
bool success = RunWithStorage(settings->GetStorage(extension_id()));
BrowserThread::PostTask(
BrowserThread::UI,
diff --git a/chrome/browser/extensions/extension_settings_api.h b/chrome/browser/extensions/extension_settings_api.h
index 1ca2180..2cc87a0 100644
--- a/chrome/browser/extensions/extension_settings_api.h
+++ b/chrome/browser/extensions/extension_settings_api.h
@@ -28,8 +28,9 @@ class SettingsFunction : public AsyncExtensionFunction {
bool UseResult(const ExtensionSettingsStorage::Result& storage_result);
private:
- // Component of RunImpl which runs on the FILE thread.
- void RunOnFileThread();
+ // Called via PostTask from RunImpl. Calls RunWithStorage and then
+ // SendReponse with its success value.
+ void RunWithSettingsOnFileThread(ExtensionSettings* settings);
};
class GetSettingsFunction : public SettingsFunction {
diff --git a/chrome/browser/extensions/extension_settings_cached_leveldb_storage_unittest.cc b/chrome/browser/extensions/extension_settings_cached_leveldb_storage_unittest.cc
index 765081a..6ae82fc 100644
--- a/chrome/browser/extensions/extension_settings_cached_leveldb_storage_unittest.cc
+++ b/chrome/browser/extensions/extension_settings_cached_leveldb_storage_unittest.cc
@@ -7,8 +7,8 @@
namespace {
ExtensionSettingsStorage* Param(
- ExtensionSettings* settings, const std::string& extension_id) {
- return settings->GetStorageForTesting(
+ const ExtensionSettings& settings, const std::string& extension_id) {
+ return settings.GetStorageForTesting(
ExtensionSettingsStorage::LEVELDB, true, extension_id);
}
diff --git a/chrome/browser/extensions/extension_settings_cached_noop_storage_unittest.cc b/chrome/browser/extensions/extension_settings_cached_noop_storage_unittest.cc
index 252b05d..8178e82 100644
--- a/chrome/browser/extensions/extension_settings_cached_noop_storage_unittest.cc
+++ b/chrome/browser/extensions/extension_settings_cached_noop_storage_unittest.cc
@@ -7,8 +7,8 @@
namespace {
ExtensionSettingsStorage* Param(
- ExtensionSettings* settings, const std::string& extension_id) {
- return settings->GetStorageForTesting(
+ const ExtensionSettings& settings, const std::string& extension_id) {
+ return settings.GetStorageForTesting(
ExtensionSettingsStorage::NOOP, true, extension_id);
}
diff --git a/chrome/browser/extensions/extension_settings_leveldb_storage_unittest.cc b/chrome/browser/extensions/extension_settings_leveldb_storage_unittest.cc
index 02dc3bd..f6fae37 100644
--- a/chrome/browser/extensions/extension_settings_leveldb_storage_unittest.cc
+++ b/chrome/browser/extensions/extension_settings_leveldb_storage_unittest.cc
@@ -7,9 +7,8 @@
namespace {
ExtensionSettingsStorage* Param(
- ExtensionSettings* settings,
- const std::string& extension_id) {
- return settings->GetStorageForTesting(
+ const ExtensionSettings& settings, const std::string& extension_id) {
+ return settings.GetStorageForTesting(
ExtensionSettingsStorage::LEVELDB, false, extension_id);
}
diff --git a/chrome/browser/extensions/extension_settings_storage.h b/chrome/browser/extensions/extension_settings_storage.h
index 5918ab2..1a5311fe 100644
--- a/chrome/browser/extensions/extension_settings_storage.h
+++ b/chrome/browser/extensions/extension_settings_storage.h
@@ -35,7 +35,7 @@ class ExtensionSettingsStorage {
// The dictionary result of the computation. NULL does not imply invalid;
// HasError() should be used to test this.
- // Wwnership remains with with the Result.
+ // Ownership remains with with the Result.
DictionaryValue* GetSettings() const;
// Whether there was an error in the computation. If so, the results of
diff --git a/chrome/browser/extensions/extension_settings_storage_unittest.cc b/chrome/browser/extensions/extension_settings_storage_unittest.cc
index fc1a361..80d6461 100644
--- a/chrome/browser/extensions/extension_settings_storage_unittest.cc
+++ b/chrome/browser/extensions/extension_settings_storage_unittest.cc
@@ -51,7 +51,9 @@ ExtensionSettingsStorageTest::ExtensionSettingsStorageTest()
empty_dict_(new DictionaryValue),
dict1_(new DictionaryValue),
dict12_(new DictionaryValue),
- dict123_(new DictionaryValue) {
+ dict123_(new DictionaryValue),
+ ui_thread_(BrowserThread::UI, MessageLoop::current()),
+ file_thread_(BrowserThread::FILE, MessageLoop::current()) {
val1_.reset(Value::CreateStringValue(key1_ + "Value"));
val2_.reset(Value::CreateStringValue(key2_ + "Value"));
val3_.reset(Value::CreateStringValue(key3_ + "Value"));
@@ -77,17 +79,16 @@ ExtensionSettingsStorageTest::ExtensionSettingsStorageTest()
ExtensionSettingsStorageTest::~ExtensionSettingsStorageTest() {}
void ExtensionSettingsStorageTest::SetUp() {
- ui_message_loop_.reset(new MessageLoopForUI());
- ui_thread_.reset(
- new BrowserThread(BrowserThread::UI, MessageLoop::current()));
- file_thread_.reset(
- new BrowserThread(BrowserThread::FILE, MessageLoop::current()));
-
- FilePath temp_dir;
- file_util::CreateNewTempDirectory(FilePath::StringType(), &temp_dir);
- settings_ = new ExtensionSettings(temp_dir);
- storage_ = (GetParam())(settings_.get(), "fakeExtension");
- DCHECK(storage_ != NULL);
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ settings_.reset(new ExtensionSettings(temp_dir_.path()));
+ storage_ = (GetParam())(*settings_, "fakeExtension");
+ ASSERT_TRUE(storage_ != NULL);
+}
+
+void ExtensionSettingsStorageTest::TearDown() {
+ // Must do this explicitly here so that it's destroyed before the
+ // message loops are.
+ settings_.reset();
}
TEST_P(ExtensionSettingsStorageTest, GetWhenEmpty) {
diff --git a/chrome/browser/extensions/extension_settings_storage_unittest.h b/chrome/browser/extensions/extension_settings_storage_unittest.h
index 1d5d666..9d209a1 100644
--- a/chrome/browser/extensions/extension_settings_storage_unittest.h
+++ b/chrome/browser/extensions/extension_settings_storage_unittest.h
@@ -11,13 +11,14 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
+#include "base/scoped_temp_dir.h"
#include "base/task.h"
#include "chrome/browser/extensions/extension_settings.h"
#include "content/browser/browser_thread.h"
// Parameter type for the value-parameterized tests.
typedef ExtensionSettingsStorage* (*ExtensionSettingsStorageTestParam)(
- ExtensionSettings* settings, const std::string& extension_id);
+ const ExtensionSettings& settings, const std::string& extension_id);
// Test fixture for ExtensionSettingsStorage tests. Tests are defined in
// extension_settings_storage_unittest.cc with configurations for both cached
@@ -29,6 +30,7 @@ class ExtensionSettingsStorageTest
virtual ~ExtensionSettingsStorageTest();
virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
protected:
ExtensionSettingsStorage* storage_;
@@ -54,12 +56,13 @@ class ExtensionSettingsStorageTest
scoped_ptr<DictionaryValue> dict123_;
private:
- scoped_refptr<ExtensionSettings> settings_;
+ ScopedTempDir temp_dir_;
+ scoped_ptr<ExtensionSettings> settings_;
// Need these so that the DCHECKs for running on FILE or UI threads pass.
- scoped_ptr<MessageLoopForUI> ui_message_loop_;
- scoped_ptr<BrowserThread> ui_thread_;
- scoped_ptr<BrowserThread> file_thread_;
+ MessageLoop message_loop_;
+ BrowserThread ui_thread_;
+ BrowserThread file_thread_;
};
#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_STORAGE_UNITTEST_H_
diff --git a/chrome/browser/extensions/extension_settings_sync_unittest.cc b/chrome/browser/extensions/extension_settings_sync_unittest.cc
new file mode 100644
index 0000000..7f77b1b
--- /dev/null
+++ b/chrome/browser/extensions/extension_settings_sync_unittest.cc
@@ -0,0 +1,492 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "base/bind.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/scoped_temp_dir.h"
+#include "base/task.h"
+#include "chrome/browser/extensions/extension_settings.h"
+#include "chrome/browser/extensions/extension_settings_storage_cache.h"
+#include "chrome/browser/extensions/extension_settings_noop_storage.h"
+#include "chrome/browser/extensions/extension_settings_sync_util.h"
+#include "chrome/browser/extensions/syncable_extension_settings_storage.h"
+#include "chrome/browser/sync/api/sync_change_processor.h"
+#include "content/browser/browser_thread.h"
+
+// TODO(kalman): Integration tests for sync.
+
+namespace {
+
+// Gets the pretty-printed JSON for a value.
+static std::string GetJson(const Value& value) {
+ std::string json;
+ base::JSONWriter::Write(&value, true, &json);
+ return json;
+}
+
+// Returns whether two Values are equal.
+testing::AssertionResult ValuesEq(
+ const char* expected_expr,
+ const char* actual_expr,
+ const Value* expected,
+ const Value* actual) {
+ if (expected == actual) {
+ return testing::AssertionSuccess();
+ }
+ if (!expected && actual) {
+ return testing::AssertionFailure() <<
+ "Expected NULL, actual: " << GetJson(*actual);
+ }
+ if (expected && !actual) {
+ return testing::AssertionFailure() <<
+ "Expected: " << GetJson(*expected) << ", actual NULL";
+ }
+ if (!expected->Equals(actual)) {
+ return testing::AssertionFailure() <<
+ "Expected: " << GetJson(*expected) << ", actual: " << GetJson(*actual);
+ }
+ return testing::AssertionSuccess();
+}
+
+// Returns whether the result of a storage operation is an expected value.
+// Logs when different.
+testing::AssertionResult SettingsEq(
+ const char* expected_expr,
+ const char* actual_expr,
+ const DictionaryValue* expected,
+ const ExtensionSettingsStorage::Result actual) {
+ if (actual.HasError()) {
+ return testing::AssertionFailure() <<
+ "Expected: " << GetJson(*expected) <<
+ ", actual has error: " << actual.GetError();
+ }
+ return ValuesEq(
+ expected_expr, actual_expr, expected, actual.GetSettings());
+}
+
+// SyncChangeProcessor which just records the changes made, accessed after
+// being converted to the more useful ExtensionSettingSyncData via changes().
+class MockSyncChangeProcessor : public SyncChangeProcessor {
+ public:
+ virtual SyncError ProcessSyncChanges(
+ const tracked_objects::Location& from_here,
+ const SyncChangeList& change_list) OVERRIDE {
+ for (SyncChangeList::const_iterator it = change_list.begin();
+ it != change_list.end(); ++it) {
+ changes_.push_back(ExtensionSettingSyncData(*it));
+ }
+ return SyncError();
+ }
+
+ const ExtensionSettingSyncDataList& changes() { return changes_; }
+
+ void ClearChanges() {
+ changes_.clear();
+ }
+
+ // Returns the only change for a given extension setting. If there is not
+ // exactly 1 change for that key, a test assertion will fail.
+ ExtensionSettingSyncData GetOnlyChange(
+ const std::string& extension_id, const std::string& key) {
+ ExtensionSettingSyncDataList matching_changes;
+ for (ExtensionSettingSyncDataList::iterator it = changes_.begin();
+ it != changes_.end(); ++it) {
+ if (it->extension_id() == extension_id && it->key() == key) {
+ matching_changes.push_back(*it);
+ }
+ }
+ if (matching_changes.empty()) {
+ ADD_FAILURE() << "No matching changes for " << extension_id << "/" <<
+ key << " (out of " << changes_.size() << ")";
+ return ExtensionSettingSyncData(SyncChange::ACTION_INVALID, "", "", NULL);
+ }
+ if (matching_changes.size() != 1u) {
+ ADD_FAILURE() << matching_changes.size() << " matching changes for " <<
+ extension_id << "/" << key << " (out of " << changes_.size() << ")";
+ }
+ return matching_changes[0];
+ }
+
+ private:
+ ExtensionSettingSyncDataList changes_;
+};
+
+} // namespace
+
+class ExtensionSettingsSyncTest : public testing::Test {
+ public:
+ ExtensionSettingsSyncTest()
+ : ui_thread_(BrowserThread::UI, MessageLoop::current()),
+ file_thread_(BrowserThread::FILE, MessageLoop::current()) {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ settings_.reset(new ExtensionSettings(temp_dir_.path()));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // Must do this explicitly here so that it's destroyed before the
+ // message loops are.
+ settings_.reset();
+ }
+
+ protected:
+ // Creates a new extension storage object and adds a record of the extension
+ // to the extension service.
+ SyncableExtensionSettingsStorage* GetStorage(
+ const std::string& extension_id) {
+ return static_cast<SyncableExtensionSettingsStorage*>(
+ settings_->GetStorage(extension_id));
+ }
+
+ MockSyncChangeProcessor sync_;
+ scoped_ptr<ExtensionSettings> settings_;
+
+ // Gets all the sync data from settings_ as a map from extension id to its
+ // sync data.
+ std::map<std::string, ExtensionSettingSyncDataList> GetAllSyncData() {
+ SyncDataList as_list =
+ settings_->GetAllSyncData(syncable::EXTENSION_SETTINGS);
+ std::map<std::string, ExtensionSettingSyncDataList> as_map;
+ for (SyncDataList::iterator it = as_list.begin();
+ it != as_list.end(); ++it) {
+ ExtensionSettingSyncData sync_data(*it);
+ as_map[sync_data.extension_id()].push_back(sync_data);
+ }
+ return as_map;
+ }
+
+ private:
+ ScopedTempDir temp_dir_;
+
+ // Need these so that the DCHECKs for running on FILE or UI threads pass.
+ MessageLoop message_loop_;
+ BrowserThread ui_thread_;
+ BrowserThread file_thread_;
+};
+
+TEST_F(ExtensionSettingsSyncTest, NoDataDoesNotInvokeSync) {
+ ASSERT_EQ(0u, GetAllSyncData().size());
+
+ // Have one extension created before sync is set up, the other created after.
+ GetStorage("s1");
+ ASSERT_EQ(0u, GetAllSyncData().size());
+
+ settings_->MergeDataAndStartSyncing(
+ syncable::EXTENSION_SETTINGS,
+ SyncDataList(),
+ &sync_);
+
+ GetStorage("s2");
+ ASSERT_EQ(0u, GetAllSyncData().size());
+
+ settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
+
+ ASSERT_EQ(0u, sync_.changes().size());
+ ASSERT_EQ(0u, GetAllSyncData().size());
+}
+
+TEST_F(ExtensionSettingsSyncTest, InSyncDataDoesNotInvokeSync) {
+ StringValue value1("fooValue");
+ ListValue value2;
+ value2.Append(StringValue::CreateStringValue("barValue"));
+
+ SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
+ SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
+
+ storage1->Set("foo", value1);
+ storage2->Set("bar", value2);
+
+ std::map<std::string, ExtensionSettingSyncDataList> all_sync_data =
+ GetAllSyncData();
+ ASSERT_EQ(2u, all_sync_data.size());
+ ASSERT_EQ(1u, all_sync_data["s1"].size());
+ ASSERT_PRED_FORMAT2(ValuesEq, &value1, &all_sync_data["s1"][0].value());
+ ASSERT_EQ(1u, all_sync_data["s2"].size());
+ ASSERT_PRED_FORMAT2(ValuesEq, &value2, &all_sync_data["s2"][0].value());
+
+ SyncDataList sync_data;
+ sync_data.push_back(extension_settings_sync_util::CreateData(
+ "s1", "foo", value1));
+ sync_data.push_back(extension_settings_sync_util::CreateData(
+ "s2", "bar", value2));
+
+ settings_->MergeDataAndStartSyncing(
+ syncable::EXTENSION_SETTINGS, sync_data, &sync_);
+ settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
+
+ // Already in sync, so no changes.
+ ASSERT_EQ(0u, sync_.changes().size());
+}
+
+TEST_F(ExtensionSettingsSyncTest, LocalDataWithNoSyncDataIsPushedToSync) {
+ StringValue value1("fooValue");
+ ListValue value2;
+ value2.Append(StringValue::CreateStringValue("barValue"));
+
+ SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
+ SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
+
+ storage1->Set("foo", value1);
+ storage2->Set("bar", value2);
+
+ settings_->MergeDataAndStartSyncing(
+ syncable::EXTENSION_SETTINGS, SyncDataList(), &sync_);
+
+ // All settings should have been pushed to sync.
+ ASSERT_EQ(2u, sync_.changes().size());
+ ExtensionSettingSyncData change = sync_.GetOnlyChange("s1", "foo");
+ ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
+ ASSERT_TRUE(value1.Equals(&change.value()));
+ change = sync_.GetOnlyChange("s2", "bar");
+ ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
+ ASSERT_TRUE(value2.Equals(&change.value()));
+
+ settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
+}
+
+TEST_F(ExtensionSettingsSyncTest, AnySyncDataOverwritesLocalData) {
+ StringValue value1("fooValue");
+ ListValue value2;
+ value2.Append(StringValue::CreateStringValue("barValue"));
+
+ // Maintain dictionaries mirrored to the expected values of the settings in
+ // each storage area.
+ DictionaryValue expected1, expected2;
+
+ // Pre-populate one of the storage areas.
+ SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
+ storage1->Set("overwriteMe", value1);
+
+ SyncDataList sync_data;
+ sync_data.push_back(extension_settings_sync_util::CreateData(
+ "s1", "foo", value1));
+ sync_data.push_back(extension_settings_sync_util::CreateData(
+ "s2", "bar", value2));
+ settings_->MergeDataAndStartSyncing(
+ syncable::EXTENSION_SETTINGS, sync_data, &sync_);
+ expected1.Set("foo", value1.DeepCopy());
+ expected2.Set("bar", value2.DeepCopy());
+
+ SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
+
+ // All changes should be local, so no sync changes.
+ ASSERT_EQ(0u, sync_.changes().size());
+
+ // Sync settings should have been pushed to local settings.
+ ASSERT_PRED_FORMAT2(SettingsEq, &expected1, storage1->Get());
+ ASSERT_PRED_FORMAT2(SettingsEq, &expected2, storage2->Get());
+
+ settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
+}
+
+TEST_F(ExtensionSettingsSyncTest, ProcessSyncChanges) {
+ StringValue value1("fooValue");
+ ListValue value2;
+ value2.Append(StringValue::CreateStringValue("barValue"));
+
+ // Maintain dictionaries mirrored to the expected values of the settings in
+ // each storage area.
+ DictionaryValue expected1, expected2;
+
+ // Make storage1 initialised from local data, storage2 initialised from sync.
+ SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
+ SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
+
+ storage1->Set("foo", value1);
+ expected1.Set("foo", value1.DeepCopy());
+
+ SyncDataList sync_data;
+ sync_data.push_back(extension_settings_sync_util::CreateData(
+ "s2", "bar", value2));
+
+ settings_->MergeDataAndStartSyncing(
+ syncable::EXTENSION_SETTINGS, sync_data, &sync_);
+ expected2.Set("bar", value2.DeepCopy());
+
+ // Make sync add some settings.
+ SyncChangeList change_list;
+ change_list.push_back(extension_settings_sync_util::CreateAdd(
+ "s1", "bar", value2));
+ change_list.push_back(extension_settings_sync_util::CreateAdd(
+ "s2", "foo", value1));
+ settings_->ProcessSyncChanges(FROM_HERE, change_list);
+ expected1.Set("bar", value2.DeepCopy());
+ expected2.Set("foo", value1.DeepCopy());
+
+ ASSERT_PRED_FORMAT2(SettingsEq, &expected1, storage1->Get());
+ ASSERT_PRED_FORMAT2(SettingsEq, &expected2, storage2->Get());
+
+ // Make sync update some settings, storage1 the new setting, storage2 the
+ // initial setting.
+ change_list.clear();
+ change_list.push_back(extension_settings_sync_util::CreateUpdate(
+ "s1", "bar", value2));
+ change_list.push_back(extension_settings_sync_util::CreateUpdate(
+ "s2", "bar", value1));
+ settings_->ProcessSyncChanges(FROM_HERE, change_list);
+ expected1.Set("bar", value2.DeepCopy());
+ expected2.Set("bar", value1.DeepCopy());
+
+ ASSERT_PRED_FORMAT2(SettingsEq, &expected1, storage1->Get());
+ ASSERT_PRED_FORMAT2(SettingsEq, &expected2, storage2->Get());
+
+ // Make sync remove some settings, storage1 the initial setting, storage2 the
+ // new setting.
+ change_list.clear();
+ change_list.push_back(extension_settings_sync_util::CreateDelete(
+ "s1", "foo"));
+ change_list.push_back(extension_settings_sync_util::CreateDelete(
+ "s2", "foo"));
+ settings_->ProcessSyncChanges(FROM_HERE, change_list);
+ expected1.Remove("foo", NULL);
+ expected2.Remove("foo", NULL);
+
+ ASSERT_PRED_FORMAT2(SettingsEq, &expected1, storage1->Get());
+ ASSERT_PRED_FORMAT2(SettingsEq, &expected2, storage2->Get());
+
+ settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
+}
+
+TEST_F(ExtensionSettingsSyncTest, PushToSync) {
+ StringValue value1("fooValue");
+ ListValue value2;
+ value2.Append(StringValue::CreateStringValue("barValue"));
+
+ // Make storage1/2 initialised from local data, storage3/4 initialised from
+ // sync.
+ SyncableExtensionSettingsStorage* storage1 = GetStorage("s1");
+ SyncableExtensionSettingsStorage* storage2 = GetStorage("s2");
+ SyncableExtensionSettingsStorage* storage3 = GetStorage("s3");
+ SyncableExtensionSettingsStorage* storage4 = GetStorage("s4");
+
+ storage1->Set("foo", value1);
+ storage2->Set("foo", value1);
+
+ SyncDataList sync_data;
+ sync_data.push_back(extension_settings_sync_util::CreateData(
+ "s3", "bar", value2));
+ sync_data.push_back(extension_settings_sync_util::CreateData(
+ "s4", "bar", value2));
+
+ settings_->MergeDataAndStartSyncing(
+ syncable::EXTENSION_SETTINGS, sync_data, &sync_);
+
+ // Add something locally.
+ storage1->Set("bar", value2);
+ storage2->Set("bar", value2);
+ storage3->Set("foo", value1);
+ storage4->Set("foo", value1);
+
+ ExtensionSettingSyncData change = sync_.GetOnlyChange("s1", "bar");
+ ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
+ ASSERT_TRUE(value2.Equals(&change.value()));
+ sync_.GetOnlyChange("s2", "bar");
+ ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
+ ASSERT_TRUE(value2.Equals(&change.value()));
+ change = sync_.GetOnlyChange("s3", "foo");
+ ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
+ ASSERT_TRUE(value1.Equals(&change.value()));
+ change = sync_.GetOnlyChange("s4", "foo");
+ ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type());
+ ASSERT_TRUE(value1.Equals(&change.value()));
+
+ // Change something locally, storage1/3 the new setting and storage2/4 the
+ // initial setting, for all combinations of local vs sync intialisation and
+ // new vs initial.
+ sync_.ClearChanges();
+ storage1->Set("bar", value1);
+ storage2->Set("foo", value2);
+ storage3->Set("bar", value1);
+ storage4->Set("foo", value2);
+
+ change = sync_.GetOnlyChange("s1", "bar");
+ ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
+ ASSERT_TRUE(value1.Equals(&change.value()));
+ change = sync_.GetOnlyChange("s2", "foo");
+ ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
+ ASSERT_TRUE(value2.Equals(&change.value()));
+ change = sync_.GetOnlyChange("s3", "bar");
+ ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
+ ASSERT_TRUE(value1.Equals(&change.value()));
+ change = sync_.GetOnlyChange("s4", "foo");
+ ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type());
+ ASSERT_TRUE(value2.Equals(&change.value()));
+
+ // Remove something locally, storage1/3 the new setting and storage2/4 the
+ // initial setting, for all combinations of local vs sync intialisation and
+ // new vs initial.
+ sync_.ClearChanges();
+ storage1->Remove("foo");
+ storage2->Remove("bar");
+ storage3->Remove("foo");
+ storage4->Remove("bar");
+
+ ASSERT_EQ(
+ SyncChange::ACTION_DELETE,
+ sync_.GetOnlyChange("s1", "foo").change_type());
+ ASSERT_EQ(
+ SyncChange::ACTION_DELETE,
+ sync_.GetOnlyChange("s2", "bar").change_type());
+ ASSERT_EQ(
+ SyncChange::ACTION_DELETE,
+ sync_.GetOnlyChange("s3", "foo").change_type());
+ ASSERT_EQ(
+ SyncChange::ACTION_DELETE,
+ sync_.GetOnlyChange("s4", "bar").change_type());
+
+ // Remove some nonexistent settings.
+ sync_.ClearChanges();
+ storage1->Remove("foo");
+ storage2->Remove("bar");
+ storage3->Remove("foo");
+ storage4->Remove("bar");
+
+ ASSERT_EQ(0u, sync_.changes().size());
+
+ // Clear the rest of the settings. Add the removed ones back first so that
+ // more than one setting is cleared.
+ storage1->Set("foo", value1);
+ storage2->Set("bar", value2);
+ storage3->Set("foo", value1);
+ storage4->Set("bar", value2);
+
+ sync_.ClearChanges();
+ storage1->Clear();
+ storage2->Clear();
+ storage3->Clear();
+ storage4->Clear();
+
+ ASSERT_EQ(
+ SyncChange::ACTION_DELETE,
+ sync_.GetOnlyChange("s1", "foo").change_type());
+ ASSERT_EQ(
+ SyncChange::ACTION_DELETE,
+ sync_.GetOnlyChange("s1", "bar").change_type());
+ ASSERT_EQ(
+ SyncChange::ACTION_DELETE,
+ sync_.GetOnlyChange("s2", "foo").change_type());
+ ASSERT_EQ(
+ SyncChange::ACTION_DELETE,
+ sync_.GetOnlyChange("s2", "bar").change_type());
+ ASSERT_EQ(
+ SyncChange::ACTION_DELETE,
+ sync_.GetOnlyChange("s3", "foo").change_type());
+ ASSERT_EQ(
+ SyncChange::ACTION_DELETE,
+ sync_.GetOnlyChange("s3", "bar").change_type());
+ ASSERT_EQ(
+ SyncChange::ACTION_DELETE,
+ sync_.GetOnlyChange("s4", "foo").change_type());
+ ASSERT_EQ(
+ SyncChange::ACTION_DELETE,
+ sync_.GetOnlyChange("s4", "bar").change_type());
+
+ settings_->StopSyncing(syncable::EXTENSION_SETTINGS);
+}
diff --git a/chrome/browser/extensions/extension_settings_sync_util.cc b/chrome/browser/extensions/extension_settings_sync_util.cc
new file mode 100644
index 0000000..d41a3e4
--- /dev/null
+++ b/chrome/browser/extensions/extension_settings_sync_util.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/extension_settings_sync_util.h"
+
+#include "base/values.h"
+#include "base/json/json_writer.h"
+#include "chrome/browser/sync/protocol/extension_setting_specifics.pb.h"
+
+namespace extension_settings_sync_util {
+
+SyncData CreateData(
+ const std::string& extension_id,
+ const std::string& key,
+ const Value& value) {
+ sync_pb::EntitySpecifics specifics;
+ sync_pb::ExtensionSettingSpecifics* setting_specifics =
+ specifics.MutableExtension(sync_pb::extension_setting);
+ setting_specifics->set_extension_id(extension_id);
+ setting_specifics->set_key(key);
+ std::string value_as_json;
+ base::JSONWriter::Write(&value, false, &value_as_json);
+ setting_specifics->set_value(value_as_json);
+ return SyncData::CreateLocalData(extension_id + "/" + key, key, specifics);
+}
+
+SyncChange CreateAdd(
+ const std::string& extension_id,
+ const std::string& key,
+ const Value& value) {
+ return SyncChange(
+ SyncChange::ACTION_ADD, CreateData(extension_id, key, value));
+}
+
+SyncChange CreateUpdate(
+ const std::string& extension_id,
+ const std::string& key,
+ const Value& value) {
+ return SyncChange(
+ SyncChange::ACTION_UPDATE, CreateData(extension_id, key, value));
+}
+
+SyncChange CreateDelete(
+ const std::string& extension_id, const std::string& key) {
+ DictionaryValue no_value;
+ return SyncChange(
+ SyncChange::ACTION_DELETE, CreateData(extension_id, key, no_value));
+}
+
+} // namespace extension_settings_sync_util
diff --git a/chrome/browser/extensions/extension_settings_sync_util.h b/chrome/browser/extensions/extension_settings_sync_util.h
new file mode 100644
index 0000000..c2107d5
--- /dev/null
+++ b/chrome/browser/extensions/extension_settings_sync_util.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_SYNC_UTIL_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_SYNC_UTIL_H_
+#pragma once
+
+
+#include "chrome/browser/sync/api/sync_data.h"
+#include "chrome/browser/sync/api/sync_change.h"
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace extension_settings_sync_util {
+
+// Creates a SyncData object for an extension setting.
+SyncData CreateData(
+ const std::string& extension_id,
+ const std::string& key,
+ const base::Value& value);
+
+// Creates an "add" sync change for an extension setting.
+SyncChange CreateAdd(
+ const std::string& extension_id,
+ const std::string& key,
+ const base::Value& value);
+
+// Creates an "update" sync change for an extension setting.
+SyncChange CreateUpdate(
+ const std::string& extension_id,
+ const std::string& key,
+ const base::Value& value);
+
+// Creates a "delete" sync change for an extension setting.
+SyncChange CreateDelete(
+ const std::string& extension_id, const std::string& key);
+
+} // namespace extension_settings_sync_util
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_SYNC_UTIL_H_
diff --git a/chrome/browser/extensions/extension_settings_ui_wrapper.cc b/chrome/browser/extensions/extension_settings_ui_wrapper.cc
new file mode 100644
index 0000000..20cad1e
--- /dev/null
+++ b/chrome/browser/extensions/extension_settings_ui_wrapper.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/extension_settings_ui_wrapper.h"
+
+#include "base/bind.h"
+#include "base/file_path.h"
+#include "chrome/browser/extensions/extension_settings.h"
+#include "content/browser/browser_thread.h"
+
+ExtensionSettingsUIWrapper::ExtensionSettingsUIWrapper(
+ const FilePath& base_path)
+ : core_(new ExtensionSettingsUIWrapper::Core()) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ BrowserThread::PostTask(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(
+ &ExtensionSettingsUIWrapper::Core::InitOnFileThread,
+ core_.get(),
+ base_path));
+}
+
+void ExtensionSettingsUIWrapper::RunWithSettings(
+ const SettingsCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ BrowserThread::PostTask(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(
+ &ExtensionSettingsUIWrapper::Core::RunWithSettingsOnFileThread,
+ core_.get(),
+ callback));
+}
+
+ExtensionSettingsUIWrapper::~ExtensionSettingsUIWrapper() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+}
+
+ExtensionSettingsUIWrapper::Core::Core()
+ : extension_settings_(NULL) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+}
+
+void ExtensionSettingsUIWrapper::Core::InitOnFileThread(
+ const FilePath& base_path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(!extension_settings_);
+ extension_settings_ = new ExtensionSettings(base_path);
+}
+
+void ExtensionSettingsUIWrapper::Core::RunWithSettingsOnFileThread(
+ const SettingsCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(extension_settings_);
+ callback.Run(extension_settings_);
+}
+
+ExtensionSettingsUIWrapper::Core::~Core() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
+ BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ BrowserThread::DeleteSoon(
+ BrowserThread::FILE, FROM_HERE, extension_settings_);
+}
diff --git a/chrome/browser/extensions/extension_settings_ui_wrapper.h b/chrome/browser/extensions/extension_settings_ui_wrapper.h
new file mode 100644
index 0000000..f5cf1fd
--- /dev/null
+++ b/chrome/browser/extensions/extension_settings_ui_wrapper.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_UI_WRAPPER_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_UI_WRAPPER_H_
+#pragma once
+
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/callback.h"
+#include "chrome/browser/sync/api/syncable_service.h"
+
+class FilePath;
+class ExtensionSettings;
+class ExtensionSettingsStorage;
+
+// Wrapper for an ExtensionSettings object for dealing with thread ownership.
+// This class lives on the UI thread while ExtensionSettings object live on
+// the FILE thread.
+class ExtensionSettingsUIWrapper {
+ public:
+ explicit ExtensionSettingsUIWrapper(const FilePath& base_path);
+
+ typedef base::Callback<void(ExtensionSettings*)> SettingsCallback;
+
+ // Runs |callback| on the FILE thread with the extension settings.
+ void RunWithSettings(const SettingsCallback& callback);
+
+ ~ExtensionSettingsUIWrapper();
+
+ private:
+ // Ref-counted container for the ExtensionSettings object.
+ class Core : public base::RefCountedThreadSafe<Core> {
+ public:
+ // Constructed on UI thread.
+ Core();
+
+ // Does any FILE thread specific initialization, such as
+ // construction of |extension_settings_|. Must be called before
+ // any call to RunWithSettingsOnFileThread().
+ void InitOnFileThread(const FilePath& base_path);
+
+ // Runs |callback| with extension settings.
+ void RunWithSettingsOnFileThread(const SettingsCallback& callback);
+
+ private:
+ // Can be destroyed on either the UI or FILE thread.
+ virtual ~Core();
+ friend class base::RefCountedThreadSafe<Core>;
+
+ // Lives on the FILE thread.
+ ExtensionSettings* extension_settings_;
+
+ DISALLOW_COPY_AND_ASSIGN(Core);
+ };
+
+ scoped_refptr<Core> core_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionSettingsUIWrapper);
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_UI_WRAPPER_H_
diff --git a/chrome/browser/extensions/syncable_extension_settings_storage.cc b/chrome/browser/extensions/syncable_extension_settings_storage.cc
new file mode 100644
index 0000000..137a6cb
--- /dev/null
+++ b/chrome/browser/extensions/syncable_extension_settings_storage.cc
@@ -0,0 +1,341 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/syncable_extension_settings_storage.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/extensions/extension_settings_sync_util.h"
+#include "chrome/browser/sync/api/sync_data.h"
+#include "chrome/browser/sync/protocol/extension_setting_specifics.pb.h"
+#include "content/browser/browser_thread.h"
+
+namespace {
+
+// Inserts all the keys from a dictionary into a set of strings.
+void InsertAll(const DictionaryValue& src, std::set<std::string>* dst) {
+ for (DictionaryValue::key_iterator it = src.begin_keys();
+ it != src.end_keys(); ++it) {
+ dst->insert(*it);
+ }
+}
+
+} // namespace
+
+SyncableExtensionSettingsStorage::SyncableExtensionSettingsStorage(
+ std::string extension_id, ExtensionSettingsStorage* delegate)
+ : extension_id_(extension_id), delegate_(delegate), sync_processor_(NULL) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+}
+
+SyncableExtensionSettingsStorage::~SyncableExtensionSettingsStorage() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+}
+
+ExtensionSettingsStorage::Result SyncableExtensionSettingsStorage::Get(
+ const std::string& key) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ return delegate_->Get(key);
+}
+
+ExtensionSettingsStorage::Result SyncableExtensionSettingsStorage::Get(
+ const std::vector<std::string>& keys) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ return delegate_->Get(keys);
+}
+
+ExtensionSettingsStorage::Result SyncableExtensionSettingsStorage::Get() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ return delegate_->Get();
+}
+
+ExtensionSettingsStorage::Result SyncableExtensionSettingsStorage::Set(
+ const std::string& key, const Value& value) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ Result result = delegate_->Set(key, value);
+ if (result.HasError()) {
+ return result;
+ }
+ if (sync_processor_) {
+ SendAddsOrUpdatesToSync(*result.GetSettings());
+ }
+ return result;
+}
+
+ExtensionSettingsStorage::Result SyncableExtensionSettingsStorage::Set(
+ const DictionaryValue& values) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ Result result = delegate_->Set(values);
+ if (result.HasError()) {
+ return result;
+ }
+ if (sync_processor_) {
+ SendAddsOrUpdatesToSync(*result.GetSettings());
+ }
+ return result;
+}
+
+ExtensionSettingsStorage::Result SyncableExtensionSettingsStorage::Remove(
+ const std::string& key) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ Result result = delegate_->Remove(key);
+ if (result.HasError()) {
+ return result;
+ }
+ if (sync_processor_) {
+ std::vector<std::string> keys;
+ keys.push_back(key);
+ SendDeletesToSync(keys);
+ }
+ return result;
+}
+
+ExtensionSettingsStorage::Result SyncableExtensionSettingsStorage::Remove(
+ const std::vector<std::string>& keys) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ Result result = delegate_->Remove(keys);
+ if (result.HasError()) {
+ return result;
+ }
+ if (sync_processor_) {
+ SendDeletesToSync(keys);
+ }
+ return result;
+}
+
+ExtensionSettingsStorage::Result SyncableExtensionSettingsStorage::Clear() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ Result result = delegate_->Clear();
+ if (result.HasError()) {
+ return result;
+ }
+ if (sync_processor_) {
+ SendDeletesToSync(
+ std::vector<std::string>(synced_keys_.begin(), synced_keys_.end()));
+ }
+ return result;
+}
+
+// Sync-related methods.
+
+SyncError SyncableExtensionSettingsStorage::StartSyncing(
+ const DictionaryValue& sync_state, SyncChangeProcessor* sync_processor) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(!sync_processor_);
+ DCHECK(synced_keys_.empty());
+ sync_processor_ = sync_processor;
+ InsertAll(sync_state, &synced_keys_);
+
+ Result maybe_settings = delegate_->Get();
+ if (maybe_settings.HasError()) {
+ return SyncError(
+ FROM_HERE,
+ std::string("Failed to get settings: ") + maybe_settings.GetError(),
+ syncable::EXTENSION_SETTINGS);
+ }
+
+ DictionaryValue* settings = maybe_settings.GetSettings();
+ if (sync_state.empty()) {
+ return SendLocalSettingsToSync(*settings);
+ }
+ return OverwriteLocalSettingsWithSync(sync_state, *settings);
+}
+
+SyncError SyncableExtensionSettingsStorage::SendLocalSettingsToSync(
+ const DictionaryValue& settings) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ SyncChangeList changes;
+ for (DictionaryValue::key_iterator it = settings.begin_keys();
+ it != settings.end_keys(); ++it) {
+ Value* value;
+ settings.GetWithoutPathExpansion(*it, &value);
+ changes.push_back(
+ extension_settings_sync_util::CreateAdd(extension_id_, *it, *value));
+ }
+
+ if (changes.empty()) {
+ return SyncError();
+ }
+
+ SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
+ if (error.IsSet()) {
+ return error;
+ }
+
+ InsertAll(settings, &synced_keys_);
+ return SyncError();
+}
+
+SyncError SyncableExtensionSettingsStorage::OverwriteLocalSettingsWithSync(
+ const DictionaryValue& sync_state, const DictionaryValue& settings) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ // Treat this as a list of changes to sync and use ProcessSyncChanges.
+ // This gives notifications etc for free.
+ scoped_ptr<DictionaryValue> new_sync_state(sync_state.DeepCopy());
+
+ ExtensionSettingSyncDataList changes;
+ for (DictionaryValue::key_iterator it = settings.begin_keys();
+ it != settings.end_keys(); ++it) {
+ Value* sync_value = NULL;
+ if (new_sync_state->RemoveWithoutPathExpansion(*it, &sync_value)) {
+ Value* local_value = NULL;
+ settings.GetWithoutPathExpansion(*it, &local_value);
+ if (!local_value->Equals(sync_value)) {
+ // Sync value is different, update local setting with new value.
+ changes.push_back(
+ ExtensionSettingSyncData(
+ SyncChange::ACTION_UPDATE, extension_id_, *it, sync_value));
+ }
+ } else {
+ // Not synced, delete local setting.
+ changes.push_back(
+ ExtensionSettingSyncData(
+ SyncChange::ACTION_DELETE,
+ extension_id_,
+ *it,
+ new DictionaryValue()));
+ }
+ }
+
+ // Add all new settings to local settings.
+ while (!new_sync_state->empty()) {
+ std::string key = *new_sync_state->begin_keys();
+ Value* value;
+ new_sync_state->RemoveWithoutPathExpansion(key, &value);
+ changes.push_back(
+ ExtensionSettingSyncData(
+ SyncChange::ACTION_ADD, extension_id_, key, value));
+ }
+
+ if (changes.empty()) {
+ return SyncError();
+ }
+
+ std::vector<SyncError> sync_errors(ProcessSyncChanges(changes));
+ if (sync_errors.empty()) {
+ return SyncError();
+ }
+ // TODO(kalman): something sensible with multiple errors.
+ return sync_errors[0];
+}
+
+void SyncableExtensionSettingsStorage::StopSyncing() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(sync_processor_);
+ sync_processor_ = NULL;
+ synced_keys_.clear();
+}
+
+std::vector<SyncError> SyncableExtensionSettingsStorage::ProcessSyncChanges(
+ const ExtensionSettingSyncDataList& sync_changes) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(sync_processor_);
+ DCHECK(!sync_changes.empty()) << "No sync changes for " << extension_id_;
+
+ std::vector<SyncError> errors;
+
+ for (ExtensionSettingSyncDataList::const_iterator it = sync_changes.begin();
+ it != sync_changes.end(); ++it) {
+ DCHECK_EQ(extension_id_, it->extension_id());
+ switch (it->change_type()) {
+ case SyncChange::ACTION_ADD:
+ case SyncChange::ACTION_UPDATE: {
+ synced_keys_.insert(it->key());
+ Result result = delegate_->Set(it->key(), it->value());
+ if (result.HasError()) {
+ errors.push_back(SyncError(
+ FROM_HERE,
+ std::string("Error pushing sync change to local settings: ") +
+ result.GetError(),
+ syncable::EXTENSION_SETTINGS));
+ }
+ break;
+ }
+
+ case SyncChange::ACTION_DELETE: {
+ synced_keys_.erase(it->key());
+ Result result = delegate_->Remove(it->key());
+ if (result.HasError()) {
+ errors.push_back(SyncError(
+ FROM_HERE,
+ std::string("Error removing sync change from local settings: ") +
+ result.GetError(),
+ syncable::EXTENSION_SETTINGS));
+ }
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ }
+ }
+
+ return errors;
+}
+
+void SyncableExtensionSettingsStorage::SendAddsOrUpdatesToSync(
+ const DictionaryValue& settings) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(sync_processor_);
+
+ SyncChangeList changes;
+ for (DictionaryValue::key_iterator it = settings.begin_keys();
+ it != settings.end_keys(); ++it) {
+ Value* value = NULL;
+ settings.GetWithoutPathExpansion(*it, &value);
+ DCHECK(value);
+ if (synced_keys_.count(*it)) {
+ // Key is synced, send ACTION_UPDATE to sync.
+ changes.push_back(
+ extension_settings_sync_util::CreateUpdate(
+ extension_id_, *it, *value));
+ } else {
+ // Key is not synced, send ACTION_ADD to sync.
+ changes.push_back(
+ extension_settings_sync_util::CreateAdd(extension_id_, *it, *value));
+ }
+ }
+
+ if (changes.empty()) {
+ return;
+ }
+
+ SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
+ if (error.IsSet()) {
+ LOG(WARNING) << "Failed to send changes to sync: " << error.message();
+ return;
+ }
+
+ InsertAll(settings, &synced_keys_);
+}
+
+void SyncableExtensionSettingsStorage::SendDeletesToSync(
+ const std::vector<std::string>& keys) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK(sync_processor_);
+
+ SyncChangeList changes;
+ for (std::vector<std::string>::const_iterator it = keys.begin();
+ it != keys.end(); ++it) {
+ // Only remove from sync if it's actually synced.
+ if (synced_keys_.count(*it)) {
+ changes.push_back(
+ extension_settings_sync_util::CreateDelete(extension_id_, *it));
+ }
+ }
+
+ if (changes.empty()) {
+ return;
+ }
+
+ SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
+ if (error.IsSet()) {
+ LOG(WARNING) << "Failed to send changes to sync: " << error.message();
+ return;
+ }
+
+ for (std::vector<std::string>::const_iterator it = keys.begin();
+ it != keys.end(); ++it) {
+ synced_keys_.erase(*it);
+ }
+}
diff --git a/chrome/browser/extensions/syncable_extension_settings_storage.h b/chrome/browser/extensions/syncable_extension_settings_storage.h
new file mode 100644
index 0000000..3232eb4
--- /dev/null
+++ b/chrome/browser/extensions/syncable_extension_settings_storage.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_SYNCABLE_EXTENSION_SETTINGS_STORAGE_H_
+#define CHROME_BROWSER_EXTENSIONS_SYNCABLE_EXTENSION_SETTINGS_STORAGE_H_
+#pragma once
+
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/extension_settings_storage.h"
+#include "chrome/browser/extensions/extension_setting_sync_data.h"
+#include "chrome/browser/sync/api/syncable_service.h"
+#include "chrome/browser/sync/api/sync_change.h"
+
+// Decorates an ExtensionSettingsStorage with sync behaviour.
+class SyncableExtensionSettingsStorage : public ExtensionSettingsStorage {
+ public:
+ explicit SyncableExtensionSettingsStorage(
+ std::string extension_id,
+ // Ownership taken.
+ ExtensionSettingsStorage* delegate);
+
+ virtual ~SyncableExtensionSettingsStorage();
+
+ // ExtensionSettingsStorage implementation.
+ virtual Result Get(const std::string& key) OVERRIDE;
+ virtual Result Get(const std::vector<std::string>& keys) OVERRIDE;
+ virtual Result Get() OVERRIDE;
+ virtual Result Set(const std::string& key, const Value& value) OVERRIDE;
+ virtual Result Set(const DictionaryValue& settings) OVERRIDE;
+ virtual Result Remove(const std::string& key) OVERRIDE;
+ virtual Result Remove(const std::vector<std::string>& keys) OVERRIDE;
+ virtual Result Clear() OVERRIDE;
+
+ // Sync-related methods, analogous to those on SyncableService (handled by
+ // ExtensionSettings).
+ SyncError StartSyncing(
+ const DictionaryValue& sync_state,
+ // Must NOT be NULL. Ownership NOT taken.
+ SyncChangeProcessor* sync_processor);
+ void StopSyncing();
+ std::vector<SyncError> ProcessSyncChanges(
+ const ExtensionSettingSyncDataList& sync_changes);
+
+ private:
+ // Either adds to sync or send updates to sync for some settings.
+ // Whether they're adds or updates depends on the state of synced_keys_.
+ void SendAddsOrUpdatesToSync(const DictionaryValue& settings);
+
+ // Sends deletes to sync for some settings.
+ void SendDeletesToSync(const std::vector<std::string>& keys);
+
+ // Sends all local settings to sync (synced settings assumed to be empty).
+ SyncError SendLocalSettingsToSync(
+ const DictionaryValue& settings);
+
+ // Overwrites local state with sync state.
+ SyncError OverwriteLocalSettingsWithSync(
+ const DictionaryValue& sync_state, const DictionaryValue& settings);
+
+ // Id of the extension these settings are for.
+ std::string const extension_id_;
+
+ // Storage area to sync.
+ const scoped_ptr<ExtensionSettingsStorage> delegate_;
+
+ // Sync processor. Non-NULL while sync is enabled (between calls to
+ // StartSyncing and StopSyncing).
+ SyncChangeProcessor* sync_processor_;
+
+ // Keys of the settings that are currently being synced.
+ std::set<std::string> synced_keys_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncableExtensionSettingsStorage);
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_SYNCABLE_EXTENSION_SETTINGS_STORAGE_H_
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 72fe06b..77deefb 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -506,7 +506,6 @@ void ProfileImpl::InitExtensions(bool extensions_enabled) {
CommandLine::ForCurrentProcess(),
GetPath().AppendASCII(ExtensionService::kInstallDirectoryName),
extension_prefs_.get(),
- extension_settings_.get(),
autoupdate_enabled,
extensions_enabled));
@@ -983,9 +982,6 @@ void ProfileImpl::OnPrefsLoaded(bool success) {
GetPath().AppendASCII(ExtensionService::kInstallDirectoryName),
GetExtensionPrefValueMap()));
- extension_settings_ = new ExtensionSettings(
- GetPath().AppendASCII(ExtensionService::kSettingsDirectoryName));
-
ProfileDependencyManager::GetInstance()->CreateProfileServices(this, false);
DCHECK(!net_pref_observer_.get());
diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h
index b22562e..d588fc1 100644
--- a/chrome/browser/profiles/profile_impl.h
+++ b/chrome/browser/profiles/profile_impl.h
@@ -198,7 +198,6 @@ class ProfileImpl : public Profile,
// Keep extension_prefs_ on top of extension_service_ because the latter
// maintains a pointer to the first and shall be destructed first.
scoped_ptr<ExtensionPrefs> extension_prefs_;
- scoped_refptr<ExtensionSettings> extension_settings_;
scoped_ptr<ExtensionService> extension_service_;
scoped_refptr<UserScriptMaster> user_script_master_;
scoped_refptr<ExtensionDevToolsManager> extension_devtools_manager_;
diff --git a/chrome/browser/sync/glue/data_type_manager_impl.cc b/chrome/browser/sync/glue/data_type_manager_impl.cc
index fb62dea..be4f368 100644
--- a/chrome/browser/sync/glue/data_type_manager_impl.cc
+++ b/chrome/browser/sync/glue/data_type_manager_impl.cc
@@ -31,6 +31,7 @@ static const syncable::ModelType kStartOrder[] = {
syncable::PREFERENCES,
syncable::AUTOFILL,
syncable::AUTOFILL_PROFILE,
+ syncable::EXTENSION_SETTINGS,
syncable::EXTENSIONS,
syncable::APPS,
syncable::THEMES,
diff --git a/chrome/browser/sync/glue/extension_setting_data_type_controller.cc b/chrome/browser/sync/glue/extension_setting_data_type_controller.cc
new file mode 100644
index 0000000..6649ca8
--- /dev/null
+++ b/chrome/browser/sync/glue/extension_setting_data_type_controller.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/glue/extension_setting_data_type_controller.h"
+
+#include "base/metrics/histogram.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_settings_ui_wrapper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/api/syncable_service.h"
+#include "chrome/browser/sync/glue/generic_change_processor.h"
+#include "chrome/browser/sync/profile_sync_factory.h"
+#include "content/browser/browser_thread.h"
+
+namespace browser_sync {
+
+ExtensionSettingDataTypeController::ExtensionSettingDataTypeController(
+ ProfileSyncFactory* profile_sync_factory,
+ Profile* profile,
+ ProfileSyncService* profile_sync_service)
+ : NonFrontendDataTypeController(profile_sync_factory, profile),
+ extension_settings_ui_wrapper_(
+ profile->GetExtensionService()->extension_settings()),
+ profile_sync_service_(profile_sync_service),
+ extension_settings_(NULL) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+}
+
+ExtensionSettingDataTypeController::~ExtensionSettingDataTypeController() {}
+
+syncable::ModelType ExtensionSettingDataTypeController::type() const {
+ return syncable::EXTENSION_SETTINGS;
+}
+
+browser_sync::ModelSafeGroup
+ExtensionSettingDataTypeController::model_safe_group() const {
+ return browser_sync::GROUP_FILE;
+}
+
+bool ExtensionSettingDataTypeController::StartModels() {
+ return true;
+}
+
+bool ExtensionSettingDataTypeController::StartAssociationAsync() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_EQ(state(), ASSOCIATING);
+ extension_settings_ui_wrapper_->RunWithSettings(
+ base::Bind(
+ &ExtensionSettingDataTypeController::
+ StartAssociationWithExtensionSettings,
+ this));
+ return true;
+}
+
+void ExtensionSettingDataTypeController::StartAssociationWithExtensionSettings(
+ ExtensionSettings* extension_settings) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ extension_settings_ = extension_settings;
+ // Calls CreateSyncComponents, which expects extension_settings_ to be
+ // non-NULL.
+ StartAssociation();
+}
+
+void ExtensionSettingDataTypeController::CreateSyncComponents() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ DCHECK_EQ(state(), ASSOCIATING);
+ DCHECK(extension_settings_);
+ ProfileSyncFactory::SyncComponents sync_components =
+ profile_sync_factory()->CreateExtensionSettingSyncComponents(
+ extension_settings_, profile_sync_service_, this);
+ set_model_associator(sync_components.model_associator);
+ set_change_processor(sync_components.change_processor);
+}
+
+bool ExtensionSettingDataTypeController::StopAssociationAsync() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_EQ(state(), STOPPING);
+ if (!BrowserThread::PostTask(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(
+ &ExtensionSettingDataTypeController::StopAssociation,
+ this))) {
+ NOTREACHED();
+ }
+ return true;
+}
+
+void ExtensionSettingDataTypeController::RecordUnrecoverableError(
+ const tracked_objects::Location& from_here,
+ const std::string& message) {
+ UMA_HISTOGRAM_COUNTS("Sync.ExtensionSettingRunFailures", 1);
+}
+
+void ExtensionSettingDataTypeController::RecordAssociationTime(
+ base::TimeDelta time) {
+ UMA_HISTOGRAM_TIMES("Sync.ExtensionSettingAssociationTime", time);
+}
+
+void ExtensionSettingDataTypeController::RecordStartFailure(
+ StartResult result) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Sync.ExtensionSettingStartFailures", result, MAX_START_RESULT);
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/extension_setting_data_type_controller.h b/chrome/browser/sync/glue/extension_setting_data_type_controller.h
new file mode 100644
index 0000000..9da3662
--- /dev/null
+++ b/chrome/browser/sync/glue/extension_setting_data_type_controller.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_GLUE_EXTENSION_SETTING_DATA_TYPE_CONTROLLER_H__
+#define CHROME_BROWSER_SYNC_GLUE_EXTENSION_SETTING_DATA_TYPE_CONTROLLER_H__
+#pragma once
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "chrome/browser/sync/glue/non_frontend_data_type_controller.h"
+
+class ExtensionSettings;
+class ExtensionSettingsUIWrapper;
+class Profile;
+class ProfileSyncFactory;
+class ProfileSyncService;
+
+namespace browser_sync {
+
+class ExtensionSettingDataTypeController
+ : public NonFrontendDataTypeController {
+ public:
+ ExtensionSettingDataTypeController(
+ ProfileSyncFactory* profile_sync_factory,
+ Profile* profile,
+ ProfileSyncService* profile_sync_service);
+ virtual ~ExtensionSettingDataTypeController();
+
+ // NonFrontendDataTypeController implementation
+ virtual syncable::ModelType type() const OVERRIDE;
+ virtual browser_sync::ModelSafeGroup model_safe_group() const OVERRIDE;
+
+ private:
+ // NonFrontendDataTypeController implementation.
+ virtual bool StartModels() OVERRIDE;
+ virtual bool StartAssociationAsync() OVERRIDE;
+ virtual void CreateSyncComponents() OVERRIDE;
+ virtual bool StopAssociationAsync() OVERRIDE;
+ virtual void RecordUnrecoverableError(
+ const tracked_objects::Location& from_here,
+ const std::string& message) OVERRIDE;
+ virtual void RecordAssociationTime(base::TimeDelta time) OVERRIDE;
+ virtual void RecordStartFailure(StartResult result) OVERRIDE;
+
+ // Starts sync association with |extension_settings|. Callback from
+ // RunWithSettings of |extension_settings_ui_wrapper_| on FILE thread.
+ void StartAssociationWithExtensionSettings(
+ ExtensionSettings* extension_settings);
+
+ // These only used on the UI thread.
+ ExtensionSettingsUIWrapper* extension_settings_ui_wrapper_;
+ ProfileSyncService* profile_sync_service_;
+
+ // Only used on the FILE thread.
+ ExtensionSettings* extension_settings_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionSettingDataTypeController);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_GLUE_EXTENSION_SETTING_DATA_TYPE_CONTROLLER_H__
diff --git a/chrome/browser/sync/profile_sync_factory.h b/chrome/browser/sync/profile_sync_factory.h
index deb089a..d60f235 100644
--- a/chrome/browser/sync/profile_sync_factory.h
+++ b/chrome/browser/sync/profile_sync_factory.h
@@ -15,6 +15,7 @@
#include "chrome/browser/sync/glue/model_associator.h"
#include "chrome/browser/sync/unrecoverable_error_handler.h"
+class ExtensionSettings;
class PersonalDataManager;
class PasswordStore;
class ProfileSyncService;
@@ -96,6 +97,14 @@ class ProfileSyncFactory {
browser_sync::UnrecoverableErrorHandler* error_handler) = 0;
// Instantiates both a model associator and change processor for the
+ // extension setting data type. The pointers in the return struct are
+ // owned by the caller.
+ virtual SyncComponents CreateExtensionSettingSyncComponents(
+ ExtensionSettings* extension_settings,
+ ProfileSyncService* profile_sync_service,
+ browser_sync::UnrecoverableErrorHandler* error_handler) = 0;
+
+ // Instantiates both a model associator and change processor for the
// extension data type. The pointers in the return struct are
// owned by the caller.
virtual SyncComponents CreateExtensionSyncComponents(
diff --git a/chrome/browser/sync/profile_sync_factory_impl.cc b/chrome/browser/sync/profile_sync_factory_impl.cc
index d470ca7..7076e38 100644
--- a/chrome/browser/sync/profile_sync_factory_impl.cc
+++ b/chrome/browser/sync/profile_sync_factory_impl.cc
@@ -4,6 +4,7 @@
#include "base/command_line.h"
#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_settings.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -19,6 +20,7 @@
#include "chrome/browser/sync/glue/bookmark_model_associator.h"
#include "chrome/browser/sync/glue/data_type_manager_impl.h"
#include "chrome/browser/sync/glue/extension_data_type_controller.h"
+#include "chrome/browser/sync/glue/extension_setting_data_type_controller.h"
#include "chrome/browser/sync/glue/generic_change_processor.h"
#include "chrome/browser/sync/glue/password_change_processor.h"
#include "chrome/browser/sync/glue/password_data_type_controller.h"
@@ -42,6 +44,7 @@
#include "chrome/browser/webdata/web_data_service.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
+#include "content/browser/browser_thread.h"
using browser_sync::AppDataTypeController;
using browser_sync::AutofillChangeProcessor;
@@ -56,6 +59,7 @@ using browser_sync::DataTypeController;
using browser_sync::DataTypeManager;
using browser_sync::DataTypeManagerImpl;
using browser_sync::ExtensionDataTypeController;
+using browser_sync::ExtensionSettingDataTypeController;
using browser_sync::GenericChangeProcessor;
using browser_sync::PasswordChangeProcessor;
using browser_sync::PasswordDataTypeController;
@@ -153,6 +157,13 @@ void ProfileSyncFactoryImpl::RegisterDataTypes(ProfileSyncService* pss) {
new SessionDataTypeController(this, profile_, pss));
}
+ // Extension setting sync is disabled by default. Register only if
+ // explicitly enabled.
+ if (command_line_->HasSwitch(switches::kEnableSyncExtensionSettings)) {
+ pss->RegisterDataTypeController(
+ new ExtensionSettingDataTypeController(this, profile_, pss));
+ }
+
if (!command_line_->HasSwitch(switches::kDisableSyncAutofillProfile)) {
pss->RegisterDataTypeController(
new AutofillProfileDataTypeController(this, profile_));
@@ -244,6 +255,22 @@ ProfileSyncFactoryImpl::CreateBookmarkSyncComponents(
}
ProfileSyncFactory::SyncComponents
+ProfileSyncFactoryImpl::CreateExtensionSettingSyncComponents(
+ ExtensionSettings* extension_settings,
+ ProfileSyncService* profile_sync_service,
+ UnrecoverableErrorHandler* error_handler) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ sync_api::UserShare* user_share = profile_sync_service->GetUserShare();
+ GenericChangeProcessor* change_processor =
+ new GenericChangeProcessor(extension_settings, error_handler, user_share);
+ browser_sync::SyncableServiceAdapter* sync_service_adapter =
+ new browser_sync::SyncableServiceAdapter(syncable::EXTENSION_SETTINGS,
+ extension_settings,
+ change_processor);
+ return SyncComponents(sync_service_adapter, change_processor);
+}
+
+ProfileSyncFactory::SyncComponents
ProfileSyncFactoryImpl::CreateExtensionSyncComponents(
ProfileSyncService* profile_sync_service,
UnrecoverableErrorHandler* error_handler) {
diff --git a/chrome/browser/sync/profile_sync_factory_impl.h b/chrome/browser/sync/profile_sync_factory_impl.h
index 1403f5d..650f407 100644
--- a/chrome/browser/sync/profile_sync_factory_impl.h
+++ b/chrome/browser/sync/profile_sync_factory_impl.h
@@ -12,6 +12,7 @@
#include "chrome/browser/sync/profile_sync_factory.h"
class CommandLine;
+class ExtensionSettings;
class Profile;
class ProfileSyncFactoryImpl : public ProfileSyncFactory {
@@ -49,6 +50,11 @@ class ProfileSyncFactoryImpl : public ProfileSyncFactory {
ProfileSyncService* profile_sync_service,
browser_sync::UnrecoverableErrorHandler* error_handler);
+ virtual SyncComponents CreateExtensionSettingSyncComponents(
+ ExtensionSettings* extension_settings,
+ ProfileSyncService* profile_sync_service,
+ browser_sync::UnrecoverableErrorHandler* error_handler);
+
virtual SyncComponents CreateExtensionSyncComponents(
ProfileSyncService* profile_sync_service,
browser_sync::UnrecoverableErrorHandler* error_handler);
diff --git a/chrome/browser/sync/profile_sync_factory_mock.h b/chrome/browser/sync/profile_sync_factory_mock.h
index ddefd39..801308a 100644
--- a/chrome/browser/sync/profile_sync_factory_mock.h
+++ b/chrome/browser/sync/profile_sync_factory_mock.h
@@ -11,6 +11,8 @@
#include "chrome/browser/sync/profile_sync_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
+class ExtensionSettings;
+
namespace browser_sync {
class AssociatorInterface;
class ChangeProcessor;
@@ -52,6 +54,10 @@ class ProfileSyncFactoryMock : public ProfileSyncFactory {
MOCK_METHOD2(CreateExtensionSyncComponents,
SyncComponents(ProfileSyncService* profile_sync_service,
browser_sync::UnrecoverableErrorHandler* error_handler));
+ MOCK_METHOD3(CreateExtensionSettingSyncComponents,
+ SyncComponents(ExtensionSettings* extension_settings,
+ ProfileSyncService* profile_sync_service,
+ browser_sync::UnrecoverableErrorHandler* error_handler));
MOCK_METHOD3(CreatePasswordSyncComponents,
SyncComponents(
ProfileSyncService* profile_sync_service,
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index 69b88ab..6600d76 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -315,6 +315,9 @@ void ProfileSyncService::RegisterPreferences() {
pref_service->RegisterBooleanPref(prefs::kSyncTypedUrls,
enable_by_default,
PrefService::UNSYNCABLE_PREF);
+ pref_service->RegisterBooleanPref(prefs::kSyncExtensionSettings,
+ enable_by_default,
+ PrefService::UNSYNCABLE_PREF);
pref_service->RegisterBooleanPref(prefs::kSyncExtensions,
enable_by_default,
PrefService::UNSYNCABLE_PREF);
@@ -580,6 +583,8 @@ const char* ProfileSyncService::GetPrefNameForDataType(
return prefs::kSyncThemes;
case syncable::TYPED_URLS:
return prefs::kSyncTypedUrls;
+ case syncable::EXTENSION_SETTINGS:
+ return prefs::kSyncExtensionSettings;
case syncable::EXTENSIONS:
return prefs::kSyncExtensions;
case syncable::APPS:
diff --git a/chrome/browser/sync/protocol/extension_setting_specifics.proto b/chrome/browser/sync/protocol/extension_setting_specifics.proto
new file mode 100644
index 0000000..ff9f029
--- /dev/null
+++ b/chrome/browser/sync/protocol/extension_setting_specifics.proto
@@ -0,0 +1,33 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Sync protocol datatype extension for an extension setting.
+
+// Update proto_value_conversions{.h,.cc,_unittest.cc} if you change
+// any fields in this file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option retain_unknown_fields = true;
+
+package sync_pb;
+
+import "sync.proto";
+
+// Properties of extension setting sync objects.
+message ExtensionSettingSpecifics {
+ // Id of the extension the setting is for.
+ optional string extension_id = 1;
+
+ // Setting key.
+ optional string key = 2;
+
+ // Setting value serialized as JSON.
+ optional string value = 3;
+}
+
+extend EntitySpecifics {
+ optional ExtensionSettingSpecifics extension_setting = 96159;
+}
diff --git a/chrome/browser/sync/protocol/nigori_specifics.proto b/chrome/browser/sync/protocol/nigori_specifics.proto
index ba18e6c..5f4d21b 100644
--- a/chrome/browser/sync/protocol/nigori_specifics.proto
+++ b/chrome/browser/sync/protocol/nigori_specifics.proto
@@ -65,6 +65,8 @@ message NigoriSpecifics {
// If true, all current and future datatypes will be encrypted.
optional bool encrypt_everything = 24;
+
+ optional bool encrypt_extension_settings = 25;
}
extend EntitySpecifics {
diff --git a/chrome/browser/sync/protocol/proto_value_conversions.cc b/chrome/browser/sync/protocol/proto_value_conversions.cc
index 1ffd123..38e40eb 100644
--- a/chrome/browser/sync/protocol/proto_value_conversions.cc
+++ b/chrome/browser/sync/protocol/proto_value_conversions.cc
@@ -15,6 +15,7 @@
#include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
#include "chrome/browser/sync/protocol/encryption.pb.h"
+#include "chrome/browser/sync/protocol/extension_setting_specifics.pb.h"
#include "chrome/browser/sync/protocol/extension_specifics.pb.h"
#include "chrome/browser/sync/protocol/nigori_specifics.pb.h"
#include "chrome/browser/sync/protocol/password_specifics.pb.h"
@@ -213,6 +214,15 @@ DictionaryValue* BookmarkSpecificsToValue(
return value;
}
+DictionaryValue* ExtensionSettingSpecificsToValue(
+ const sync_pb::ExtensionSettingSpecifics& proto) {
+ DictionaryValue* value = new DictionaryValue();
+ SET_STR(extension_id);
+ SET_STR(key);
+ SET_STR(value);
+ return value;
+}
+
DictionaryValue* ExtensionSpecificsToValue(
const sync_pb::ExtensionSpecifics& proto) {
DictionaryValue* value = new DictionaryValue();
@@ -242,6 +252,7 @@ DictionaryValue* NigoriSpecificsToValue(
SET_BOOL(encrypt_search_engines);
SET_BOOL(sync_tabs);
SET_BOOL(encrypt_everything);
+ SET_BOOL(encrypt_extension_settings);
return value;
}
@@ -321,6 +332,7 @@ DictionaryValue* EntitySpecificsToValue(
SET_EXTENSION(sync_pb, autofill_profile, AutofillProfileSpecificsToValue);
SET_EXTENSION(sync_pb, bookmark, BookmarkSpecificsToValue);
SET_EXTENSION(sync_pb, extension, ExtensionSpecificsToValue);
+ SET_EXTENSION(sync_pb, extension_setting, ExtensionSettingSpecificsToValue);
SET_EXTENSION(sync_pb, nigori, NigoriSpecificsToValue);
SET_EXTENSION(sync_pb, password, PasswordSpecificsToValue);
SET_EXTENSION(sync_pb, preference, PreferenceSpecificsToValue);
diff --git a/chrome/browser/sync/protocol/proto_value_conversions.h b/chrome/browser/sync/protocol/proto_value_conversions.h
index e9da22c..5556d3f 100644
--- a/chrome/browser/sync/protocol/proto_value_conversions.h
+++ b/chrome/browser/sync/protocol/proto_value_conversions.h
@@ -19,6 +19,7 @@ class AutofillSpecifics;
class BookmarkSpecifics;
class EncryptedData;
class EntitySpecifics;
+class ExtensionSettingSpecifics;
class ExtensionSpecifics;
class NigoriSpecifics;
class PasswordSpecifics;
@@ -85,6 +86,9 @@ base::DictionaryValue* AutofillProfileSpecificsToValue(
base::DictionaryValue* BookmarkSpecificsToValue(
const sync_pb::BookmarkSpecifics& bookmark_specifics);
+base::DictionaryValue* ExtensionSettingSpecificsToValue(
+ const sync_pb::ExtensionSettingSpecifics& extension_setting_specifics);
+
base::DictionaryValue* ExtensionSpecificsToValue(
const sync_pb::ExtensionSpecifics& extension_specifics);
diff --git a/chrome/browser/sync/protocol/proto_value_conversions_unittest.cc b/chrome/browser/sync/protocol/proto_value_conversions_unittest.cc
index 2a1f4e8..9f85960 100644
--- a/chrome/browser/sync/protocol/proto_value_conversions_unittest.cc
+++ b/chrome/browser/sync/protocol/proto_value_conversions_unittest.cc
@@ -12,6 +12,7 @@
#include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
#include "chrome/browser/sync/protocol/encryption.pb.h"
+#include "chrome/browser/sync/protocol/extension_setting_specifics.pb.h"
#include "chrome/browser/sync/protocol/extension_specifics.pb.h"
#include "chrome/browser/sync/protocol/nigori_specifics.pb.h"
#include "chrome/browser/sync/protocol/password_specifics.pb.h"
@@ -44,7 +45,7 @@ TEST_F(ProtoValueConversionsTest, ProtoChangeCheck) {
// If this number changes, that means we added or removed a data
// type. Don't forget to add a unit test for {New
// type}SpecificsToValue below.
- EXPECT_EQ(14, syncable::MODEL_TYPE_COUNT);
+ EXPECT_EQ(15, syncable::MODEL_TYPE_COUNT);
// We'd also like to check if we changed any field in our messages.
// However, that's hard to do: sizeof could work, but it's
@@ -99,6 +100,10 @@ TEST_F(ProtoValueConversionsTest, BookmarkSpecificsToValue) {
TestSpecificsToValue(BookmarkSpecificsToValue);
}
+TEST_F(ProtoValueConversionsTest, ExtensionSettingSpecificsToValue) {
+ TestSpecificsToValue(ExtensionSettingSpecificsToValue);
+}
+
TEST_F(ProtoValueConversionsTest, ExtensionSpecificsToValue) {
TestSpecificsToValue(ExtensionSpecificsToValue);
}
@@ -144,6 +149,7 @@ TEST_F(ProtoValueConversionsTest, EntitySpecificsToValue) {
SET_EXTENSION(autofill_profile);
SET_EXTENSION(bookmark);
SET_EXTENSION(extension);
+ SET_EXTENSION(extension_setting);
SET_EXTENSION(nigori);
SET_EXTENSION(password);
SET_EXTENSION(preference);
diff --git a/chrome/browser/sync/protocol/sync_proto.gyp b/chrome/browser/sync/protocol/sync_proto.gyp
index ad60328..add6a9f 100644
--- a/chrome/browser/sync/protocol/sync_proto.gyp
+++ b/chrome/browser/sync/protocol/sync_proto.gyp
@@ -16,6 +16,7 @@
'app_specifics.proto',
'autofill_specifics.proto',
'bookmark_specifics.proto',
+ 'extension_setting_specifics.proto',
'extension_specifics.proto',
'nigori_specifics.proto',
'password_specifics.proto',
diff --git a/chrome/browser/sync/syncable/model_type.cc b/chrome/browser/sync/syncable/model_type.cc
index 8d4227d..9c186eb 100644
--- a/chrome/browser/sync/syncable/model_type.cc
+++ b/chrome/browser/sync/syncable/model_type.cc
@@ -11,6 +11,7 @@
#include "chrome/browser/sync/protocol/app_specifics.pb.h"
#include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
+#include "chrome/browser/sync/protocol/extension_setting_specifics.pb.h"
#include "chrome/browser/sync/protocol/extension_specifics.pb.h"
#include "chrome/browser/sync/protocol/nigori_specifics.pb.h"
#include "chrome/browser/sync/protocol/password_specifics.pb.h"
@@ -62,6 +63,9 @@ void AddDefaultExtensionValue(syncable::ModelType datatype,
case APPS:
specifics->MutableExtension(sync_pb::app);
break;
+ case EXTENSION_SETTINGS:
+ specifics->MutableExtension(sync_pb::extension_setting);
+ break;
default:
NOTREACHED() << "No known extension for model type.";
}
@@ -115,6 +119,9 @@ int GetExtensionFieldNumberFromModelType(ModelType model_type) {
case APPS:
return sync_pb::kAppFieldNumber;
break;
+ case EXTENSION_SETTINGS:
+ return sync_pb::kExtensionSettingFieldNumber;
+ break;
default:
NOTREACHED() << "No known extension for model type.";
return 0;
@@ -192,6 +199,9 @@ ModelType GetModelTypeFromSpecifics(const sync_pb::EntitySpecifics& specifics) {
if (specifics.HasExtension(sync_pb::session))
return SESSIONS;
+ if (specifics.HasExtension(sync_pb::extension_setting))
+ return EXTENSION_SETTINGS;
+
return UNSPECIFIED;
}
@@ -225,6 +235,8 @@ std::string ModelTypeToString(ModelType model_type) {
return "Apps";
case AUTOFILL_PROFILE:
return "Autofill Profiles";
+ case EXTENSION_SETTINGS:
+ return "Extension settings";
default:
break;
}
@@ -295,6 +307,8 @@ ModelType ModelTypeFromString(const std::string& model_type_string) {
return SESSIONS;
else if (model_type_string == "Apps")
return APPS;
+ else if (model_type_string == "Extension settings")
+ return EXTENSION_SETTINGS;
else
NOTREACHED() << "No known model type corresponding to "
<< model_type_string << ".";
@@ -387,6 +401,8 @@ std::string ModelTypeToRootTag(ModelType type) {
return "google_chrome_apps";
case AUTOFILL_PROFILE:
return "google_chrome_autofill_profiles";
+ case EXTENSION_SETTINGS:
+ return "google_chrome_extension_settings";
default:
break;
}
@@ -450,6 +466,10 @@ void PostTimeToTypeHistogram(ModelType model_type, base::TimeDelta time) {
SYNC_FREQ_HISTOGRAM("Sync.FreqApps", time);
return;
}
+ case EXTENSION_SETTINGS: {
+ SYNC_FREQ_HISTOGRAM("Sync.FreqExtensionSettings", time);
+ return;
+ }
default:
LOG(ERROR) << "No known extension for model type.";
}
@@ -467,6 +487,7 @@ const char kAutofillNotificationType[] = "AUTOFILL";
const char kThemeNotificationType[] = "THEME";
const char kTypedUrlNotificationType[] = "TYPED_URL";
const char kExtensionNotificationType[] = "EXTENSION";
+const char kExtensionSettingNotificationType[] = "EXTENSION_SETTING";
const char kNigoriNotificationType[] = "NIGORI";
const char kAppNotificationType[] = "APP";
const char kSearchEngineNotificationType[] = "SEARCH_ENGINE";
@@ -513,6 +534,9 @@ bool RealModelTypeToNotificationType(ModelType model_type,
case AUTOFILL_PROFILE:
*notification_type = kAutofillProfileNotificationType;
return true;
+ case EXTENSION_SETTINGS:
+ *notification_type = kExtensionSettingNotificationType;
+ return true;
default:
break;
}
@@ -558,6 +582,9 @@ bool NotificationTypeToRealModelType(const std::string& notification_type,
} else if (notification_type == kAutofillProfileNotificationType) {
*model_type = AUTOFILL_PROFILE;
return true;
+ } else if (notification_type == kExtensionSettingNotificationType) {
+ *model_type = EXTENSION_SETTINGS;
+ return true;
}
*model_type = UNSPECIFIED;
return false;
diff --git a/chrome/browser/sync/syncable/model_type.h b/chrome/browser/sync/syncable/model_type.h
index b4285e5..09f4410 100644
--- a/chrome/browser/sync/syncable/model_type.h
+++ b/chrome/browser/sync/syncable/model_type.h
@@ -74,6 +74,8 @@ enum ModelType {
SESSIONS,
// An app folder or an app object.
APPS,
+ // A setting from the extension settings API.
+ EXTENSION_SETTINGS,
MODEL_TYPE_COUNT,
};
diff --git a/chrome/browser/sync/util/cryptographer.cc b/chrome/browser/sync/util/cryptographer.cc
index 8126f48..5e2a129 100644
--- a/chrome/browser/sync/util/cryptographer.cc
+++ b/chrome/browser/sync/util/cryptographer.cc
@@ -291,6 +291,8 @@ void Cryptographer::UpdateEncryptedTypesFromNigori(
encrypted_types_.insert(syncable::THEMES);
if (nigori.encrypt_typed_urls())
encrypted_types_.insert(syncable::TYPED_URLS);
+ if (nigori.encrypt_extension_settings())
+ encrypted_types_.insert(syncable::EXTENSION_SETTINGS);
if (nigori.encrypt_extensions())
encrypted_types_.insert(syncable::EXTENSIONS);
if (nigori.encrypt_search_engines())
@@ -324,6 +326,8 @@ void Cryptographer::UpdateNigoriFromEncryptedTypes(
nigori->set_encrypt_themes(encrypted_types_.count(syncable::THEMES) > 0);
nigori->set_encrypt_typed_urls(
encrypted_types_.count(syncable::TYPED_URLS) > 0);
+ nigori->set_encrypt_extension_settings(
+ encrypted_types_.count(syncable::EXTENSION_SETTINGS) > 0);
nigori->set_encrypt_extensions(
encrypted_types_.count(syncable::EXTENSIONS) > 0);
nigori->set_encrypt_search_engines(
diff --git a/chrome/browser/ui/cocoa/extensions/extension_popup_controller_unittest.mm b/chrome/browser/ui/cocoa/extensions/extension_popup_controller_unittest.mm
index 88363dc..499c1a9 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_popup_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/extensions/extension_popup_controller_unittest.mm
@@ -9,7 +9,6 @@
#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_settings.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/cocoa/cocoa_test_helper.h"
#include "chrome/browser/ui/cocoa/extensions/extension_popup_controller.h"
@@ -38,12 +37,10 @@ class ExtensionTestingProfile : public TestingProfile {
extension_prefs_.reset(new ExtensionPrefs(GetPrefs(),
GetExtensionsInstallDir(),
extension_pref_value_map_.get()));
- extension_settings_ = new ExtensionSettings(GetExtensionsSettingsDir());
service_.reset(new ExtensionService(this,
CommandLine::ForCurrentProcess(),
GetExtensionsInstallDir(),
extension_prefs_.get(),
- extension_settings_.get(),
false,
true));
service_->set_extensions_enabled(true);
@@ -56,7 +53,6 @@ class ExtensionTestingProfile : public TestingProfile {
manager_.reset();
service_.reset();
extension_prefs_.reset();
- extension_settings_ = NULL;
}
virtual ExtensionProcessManager* GetExtensionProcessManager() {
@@ -70,7 +66,6 @@ class ExtensionTestingProfile : public TestingProfile {
private:
scoped_ptr<ExtensionProcessManager> manager_;
scoped_ptr<ExtensionPrefs> extension_prefs_;
- scoped_refptr<ExtensionSettings> extension_settings_;
scoped_ptr<ExtensionService> service_;
scoped_ptr<ExtensionPrefValueMap> extension_pref_value_map_;
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 328ae5e..5b58dc4 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1069,8 +1069,12 @@
'browser/extensions/extension_proxy_api_helpers.h',
'browser/extensions/extension_rlz_module.cc',
'browser/extensions/extension_rlz_module.h',
+ 'browser/extensions/extension_setting_sync_data.cc',
+ 'browser/extensions/extension_setting_sync_data.h',
'browser/extensions/extension_settings.cc',
'browser/extensions/extension_settings.h',
+ 'browser/extensions/extension_settings_ui_wrapper.cc',
+ 'browser/extensions/extension_settings_ui_wrapper.h',
'browser/extensions/extension_settings_api.cc',
'browser/extensions/extension_settings_api.h',
'browser/extensions/extension_settings_noop_storage.cc',
@@ -1079,6 +1083,8 @@
'browser/extensions/extension_settings_storage.h',
'browser/extensions/extension_settings_storage_cache.cc',
'browser/extensions/extension_settings_storage_cache.h',
+ 'browser/extensions/extension_settings_sync_util.cc',
+ 'browser/extensions/extension_settings_sync_util.h',
'browser/extensions/extension_settings_storage_quota_enforcer.cc',
'browser/extensions/extension_settings_storage_quota_enforcer.h',
'browser/extensions/extension_settings_leveldb_storage.cc',
@@ -1170,6 +1176,8 @@
'browser/extensions/pending_extension_manager.h',
'browser/extensions/sandboxed_extension_unpacker.cc',
'browser/extensions/sandboxed_extension_unpacker.h',
+ 'browser/extensions/syncable_extension_settings_storage.cc',
+ 'browser/extensions/syncable_extension_settings_storage.h',
'browser/extensions/theme_installed_infobar_delegate.cc',
'browser/extensions/theme_installed_infobar_delegate.h',
'browser/extensions/user_script_listener.cc',
@@ -2084,6 +2092,8 @@
'browser/sync/glue/do_optimistic_refresh_task.h',
'browser/sync/glue/extension_data_type_controller.cc',
'browser/sync/glue/extension_data_type_controller.h',
+ 'browser/sync/glue/extension_setting_data_type_controller.cc',
+ 'browser/sync/glue/extension_setting_data_type_controller.h',
'browser/sync/glue/frontend_data_type_controller.cc',
'browser/sync/glue/frontend_data_type_controller.h',
'browser/sync/glue/generic_change_processor.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 33c37c6..a7b47d3 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1243,6 +1243,13 @@
'browser/extensions/extension_prefs_unittest.cc',
'browser/extensions/extension_process_manager_unittest.cc',
'browser/extensions/extension_proxy_api_helpers_unittest.cc',
+ 'browser/extensions/extension_settings_cached_leveldb_storage_unittest.cc',
+ 'browser/extensions/extension_settings_cached_noop_storage_unittest.cc',
+ 'browser/extensions/extension_settings_leveldb_storage_unittest.cc',
+ 'browser/extensions/extension_settings_quota_unittest.cc',
+ 'browser/extensions/extension_settings_storage_unittest.h',
+ 'browser/extensions/extension_settings_storage_unittest.cc',
+ 'browser/extensions/extension_settings_sync_unittest.cc',
'browser/extensions/extension_service_unittest.cc',
'browser/extensions/extension_service_unittest.h',
'browser/extensions/extension_special_storage_policy_unittest.cc',
@@ -2318,12 +2325,6 @@
'browser/extensions/extension_resource_request_policy_apitest.cc',
'browser/extensions/extension_rlz_apitest.cc',
'browser/extensions/extension_settings_apitest.cc',
- 'browser/extensions/extension_settings_cached_leveldb_storage_unittest.cc',
- 'browser/extensions/extension_settings_cached_noop_storage_unittest.cc',
- 'browser/extensions/extension_settings_leveldb_storage_unittest.cc',
- 'browser/extensions/extension_settings_quota_unittest.cc',
- 'browser/extensions/extension_settings_storage_unittest.h',
- 'browser/extensions/extension_settings_storage_unittest.cc',
'browser/extensions/extension_sidebar_apitest.cc',
'browser/extensions/extension_startup_browsertest.cc',
'browser/extensions/extension_storage_apitest.cc',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index f45b0f5..779030a 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -503,6 +503,10 @@ const char kEnableShortcutsProvider[] = "enable-shortcuts-provider";
// On platforms that support it, enable smooth scroll animation.
const char kEnableSmoothScrolling[] = "enable-smooth-scrolling";
+
+// Enable syncing extension settings.
+const char kEnableSyncExtensionSettings[] = "enable-sync-extension-settings";
+
// Enable OAuth sign-in for sync.
const char kEnableSyncOAuth[] = "enable-sync-oauth";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 4338e3d..d080fbd 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -145,6 +145,9 @@ extern const char kEnableResourceContentSettings[];
extern const char kEnableSearchProviderApiV2[];
extern const char kEnableShortcutsProvider[];
extern const char kEnableSmoothScrolling[];
+// TODO(kalman): Add to about:flags when UI for syncing extension settings has
+// been figured out.
+extern const char kEnableSyncExtensionSettings[];
extern const char kEnableSyncOAuth[];
extern const char kEnableSyncSearchEngines[];
extern const char kEnableSyncTabs[];
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index ae2e69a..75471c6 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1366,6 +1366,7 @@ const char kSyncAutofillProfile[] = "sync.autofill_profile";
const char kSyncThemes[] = "sync.themes";
const char kSyncTypedUrls[] = "sync.typed_urls";
const char kSyncExtensions[] = "sync.extensions";
+const char kSyncExtensionSettings[] = "sync.extension_settings";
const char kSyncSearchEngines[] = "sync.search_engines";
const char kSyncSessions[] = "sync.sessions";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 9def19e..bb22ef7 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -494,6 +494,7 @@ extern const char kSyncAutofillProfile[];
extern const char kSyncThemes[];
extern const char kSyncTypedUrls[];
extern const char kSyncExtensions[];
+extern const char kSyncExtensionSettings[];
extern const char kSyncManaged[];
extern const char kSyncSearchEngines[];
extern const char kSyncSessions[];
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index 2c07469..d75727a 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -347,13 +347,10 @@ ExtensionService* TestingProfile::CreateExtensionService(
new ExtensionPrefs(GetPrefs(),
install_directory,
extension_pref_value_map_.get()));
- extension_settings_ =
- new ExtensionSettings(GetPath().AppendASCII("Extension Settings"));
extension_service_.reset(new ExtensionService(this,
command_line,
install_directory,
extension_prefs_.get(),
- extension_settings_.get(),
autoupdate_enabled,
true));
return extension_service_.get();
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index 1f9b094..39aefe2 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -34,7 +34,6 @@ class SpecialStoragePolicy;
class AutocompleteClassifier;
class BookmarkModel;
class CommandLine;
-class ExtensionSettings;
class ExtensionPrefs;
class ExtensionPrefStore;
class ExtensionPrefValueMap;
@@ -377,10 +376,6 @@ class TestingProfile : public Profile {
// invoked.
scoped_ptr<ExtensionPrefs> extension_prefs_;
- // The Extension settings. Only created if CreateExtensionService is
- // invoked.
- scoped_refptr<ExtensionSettings> extension_settings_;
-
scoped_ptr<ExtensionService> extension_service_;
scoped_ptr<ExtensionPrefValueMap> extension_pref_value_map_;
diff --git a/net/tools/testserver/chromiumsync.py b/net/tools/testserver/chromiumsync.py
index a8a939c..e1c049c 100755
--- a/net/tools/testserver/chromiumsync.py
+++ b/net/tools/testserver/chromiumsync.py
@@ -21,6 +21,7 @@ import urlparse
import app_specifics_pb2
import autofill_specifics_pb2
import bookmark_specifics_pb2
+import extension_setting_specifics_pb2
import extension_specifics_pb2
import nigori_specifics_pb2
import password_specifics_pb2
@@ -48,7 +49,8 @@ ALL_TYPES = (
SEARCH_ENGINE,
SESSION,
THEME,
- TYPED_URL) = range(13)
+ TYPED_URL,
+ EXTENSION_SETTINGS) = range(14)
# Well-known server tag of the top level 'Google Chrome' folder.
TOP_LEVEL_FOLDER_TAG = 'google_chrome'
@@ -60,6 +62,7 @@ SYNC_TYPE_TO_EXTENSION = {
AUTOFILL: autofill_specifics_pb2.autofill,
AUTOFILL_PROFILE: autofill_specifics_pb2.autofill_profile,
BOOKMARK: bookmark_specifics_pb2.bookmark,
+ EXTENSION_SETTINGS: extension_setting_specifics_pb2.extension_setting,
EXTENSIONS: extension_specifics_pb2.extension,
NIGORI: nigori_specifics_pb2.nigori,
PASSWORD: password_specifics_pb2.password,
@@ -384,6 +387,9 @@ class SyncDataModel(object):
parent_tag='google_chrome', sync_type=AUTOFILL),
PermanentItem('google_chrome_autofill_profiles', name='Autofill Profiles',
parent_tag='google_chrome', sync_type=AUTOFILL_PROFILE),
+ PermanentItem('google_chrome_extension_settings',
+ name='Extension Settings',
+ parent_tag='google_chrome', sync_type=EXTENSION_SETTINGS),
PermanentItem('google_chrome_extensions', name='Extensions',
parent_tag='google_chrome', sync_type=EXTENSIONS),
PermanentItem('google_chrome_passwords', name='Passwords',