diff options
author | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-24 04:17:24 +0000 |
---|---|---|
committer | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-24 04:17:24 +0000 |
commit | 15d5b476275921317b88494f81ce08d16e936c71 (patch) | |
tree | 046c185a3e42df4883ed256bd6695ada54d94203 /chrome | |
parent | b77576f54910cfb2e64ef91f3b3bb197136553b0 (diff) | |
download | chromium_src-15d5b476275921317b88494f81ce08d16e936c71.zip chromium_src-15d5b476275921317b88494f81ce08d16e936c71.tar.gz chromium_src-15d5b476275921317b88494f81ce08d16e936c71.tar.bz2 |
Extension Settings API: move the API functions into an object SettingsNamepace,
and separate the API into a "sync" and a "local" namespace. Make calls into
the "local" namespace not synced.
BUG=103863
TEST=*ExtensionSetting* (browser_tests, unit_tests)
Review URL: http://codereview.chromium.org/8670012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111494 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
29 files changed, 1480 insertions, 828 deletions
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index 009dedb..54dd0c2 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc @@ -169,9 +169,15 @@ ExtensionService::NaClModuleInfo::~NaClModuleInfo() { // ExtensionService. const char* ExtensionService::kInstallDirectoryName = "Extensions"; -const char* ExtensionService::kExtensionSettingsDirectoryName = - "Extension Settings"; -const char* ExtensionService::kAppSettingsDirectoryName = "App Settings"; + +const char* ExtensionService::kLocalAppSettingsDirectoryName = + "Local App Settings"; +const char* ExtensionService::kLocalExtensionSettingsDirectoryName = + "Local Extension Settings"; +const char* ExtensionService::kSyncAppSettingsDirectoryName = + "Sync App Settings"; +const char* ExtensionService::kSyncExtensionSettingsDirectoryName = + "Sync Extension Settings"; void ExtensionService::CheckExternalUninstall(const std::string& id) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h index 677fb19..e68deb35 100644 --- a/chrome/browser/extensions/extension_service.h +++ b/chrome/browser/extensions/extension_service.h @@ -140,13 +140,21 @@ class ExtensionService // If auto-updates are turned on, default to running every 5 hours. static const int kDefaultUpdateFrequencySeconds = 60 * 60 * 5; - // The name of the directory inside the profile where per-extension settings + // The name of the directory inside the profile where per-app local settings // are stored. - static const char* kExtensionSettingsDirectoryName; + static const char* kLocalAppSettingsDirectoryName; - // The name of the directory inside the profile where per-app settings + // The name of the directory inside the profile where per-extension local + // settings are stored. + static const char* kLocalExtensionSettingsDirectoryName; + + // The name of the directory inside the profile where per-app synced settings // are stored. - static const char* kAppSettingsDirectoryName; + static const char* kSyncAppSettingsDirectoryName; + + // The name of the directory inside the profile where per-extension synced + // settings are stored. + static const char* kSyncExtensionSettingsDirectoryName; // Determine if a given extension download should be treated as if it came // from the gallery. Note that this is requires *both* that the download_url diff --git a/chrome/browser/extensions/settings/settings_api.cc b/chrome/browser/extensions/settings/settings_api.cc index 2d29288..ba277ff 100644 --- a/chrome/browser/extensions/settings/settings_api.cc +++ b/chrome/browser/extensions/settings/settings_api.cc @@ -23,23 +23,36 @@ const char* kUnsupportedArgumentType = "Unsupported argument type"; // SettingsFunction +SettingsFunction::SettingsFunction() + : settings_namespace_(settings_namespace::INVALID) {} + +SettingsFunction::~SettingsFunction() {} + bool SettingsFunction::RunImpl() { + { + std::string settings_namespace_string; + EXTENSION_FUNCTION_VALIDATE( + args_->GetString(0, &settings_namespace_string)); + args_->Remove(0, NULL); + settings_namespace_ = + settings_namespace::FromString(settings_namespace_string); + EXTENSION_FUNCTION_VALIDATE( + settings_namespace_ != settings_namespace::INVALID); + } + SettingsFrontend* frontend = profile()->GetExtensionService()->settings_frontend(); + observers_ = frontend->GetObservers(); frontend->RunWithStorage( extension_id(), - base::Bind( - &SettingsFunction::RunWithStorageOnFileThread, - this, - frontend->GetObservers())); + settings_namespace_, + base::Bind(&SettingsFunction::RunWithStorageOnFileThread, this)); return true; } -void SettingsFunction::RunWithStorageOnFileThread( - scoped_refptr<SettingsObserverList> observers, - SettingsStorage* storage) { +void SettingsFunction::RunWithStorageOnFileThread(SettingsStorage* storage) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - bool success = RunWithStorage(observers.get(), storage); + bool success = RunWithStorage(storage); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -59,7 +72,6 @@ bool SettingsFunction::UseReadResult( } bool SettingsFunction::UseWriteResult( - scoped_refptr<SettingsObserverList> observers, const SettingsStorage::WriteResult& result) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); if (result.HasError()) { @@ -68,9 +80,10 @@ bool SettingsFunction::UseWriteResult( } if (!result.changes().empty()) { - observers->Notify( + observers_->Notify( &SettingsObserver::OnSettingsChanged, extension_id(), + settings_namespace_, SettingChange::GetEventJson(result.changes())); } @@ -128,9 +141,7 @@ static void GetModificationQuotaLimitHeuristics( } // namespace -bool GetSettingsFunction::RunWithStorage( - scoped_refptr<SettingsObserverList> observers, - SettingsStorage* storage) { +bool GetSettingsFunction::RunWithStorage(SettingsStorage* storage) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); Value *input; EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input)); @@ -171,14 +182,11 @@ bool GetSettingsFunction::RunWithStorage( } } -bool SetSettingsFunction::RunWithStorage( - scoped_refptr<SettingsObserverList> observers, - SettingsStorage* storage) { +bool SetSettingsFunction::RunWithStorage(SettingsStorage* storage) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DictionaryValue *input; + DictionaryValue* input; EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &input)); - return UseWriteResult( - observers, storage->Set(SettingsStorage::DEFAULTS, *input)); + return UseWriteResult(storage->Set(SettingsStorage::DEFAULTS, *input)); } void SetSettingsFunction::GetQuotaLimitHeuristics( @@ -186,9 +194,7 @@ void SetSettingsFunction::GetQuotaLimitHeuristics( GetModificationQuotaLimitHeuristics(heuristics); } -bool RemoveSettingsFunction::RunWithStorage( - scoped_refptr<SettingsObserverList> observers, - SettingsStorage* storage) { +bool RemoveSettingsFunction::RunWithStorage(SettingsStorage* storage) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); Value *input; EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input)); @@ -197,18 +203,17 @@ bool RemoveSettingsFunction::RunWithStorage( case Value::TYPE_STRING: { std::string as_string; input->GetAsString(&as_string); - return UseWriteResult(observers, storage->Remove(as_string)); + return UseWriteResult(storage->Remove(as_string)); } case Value::TYPE_LIST: { std::vector<std::string> as_string_list; AddAllStringValues(*static_cast<ListValue*>(input), &as_string_list); - return UseWriteResult(observers, storage->Remove(as_string_list)); + return UseWriteResult(storage->Remove(as_string_list)); } default: return UseWriteResult( - observers, SettingsStorage::WriteResult(kUnsupportedArgumentType)); }; } @@ -218,11 +223,9 @@ void RemoveSettingsFunction::GetQuotaLimitHeuristics( GetModificationQuotaLimitHeuristics(heuristics); } -bool ClearSettingsFunction::RunWithStorage( - scoped_refptr<SettingsObserverList> observers, - SettingsStorage* storage) { +bool ClearSettingsFunction::RunWithStorage(SettingsStorage* storage) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - return UseWriteResult(observers, storage->Clear()); + return UseWriteResult(storage->Clear()); } void ClearSettingsFunction::GetQuotaLimitHeuristics( diff --git a/chrome/browser/extensions/settings/settings_api.h b/chrome/browser/extensions/settings/settings_api.h index 9e0c712..a73e5fe 100644 --- a/chrome/browser/extensions/settings/settings_api.h +++ b/chrome/browser/extensions/settings/settings_api.h @@ -10,12 +10,17 @@ #include "base/memory/ref_counted.h" #include "chrome/browser/extensions/extension_function.h" #include "chrome/browser/extensions/settings/settings_backend.h" +#include "chrome/browser/extensions/settings/settings_namespace.h" #include "chrome/browser/extensions/settings/settings_storage.h" namespace extensions { // Superclass of all settings functions. class SettingsFunction : public AsyncExtensionFunction { + public: + SettingsFunction(); + virtual ~SettingsFunction(); + protected: virtual bool RunImpl() OVERRIDE; @@ -24,28 +29,28 @@ class SettingsFunction : public AsyncExtensionFunction { // // Implementations should fill in args themselves, though (like RunImpl) // may return false to imply failure. - virtual bool RunWithStorage( - scoped_refptr<SettingsObserverList> observers, - SettingsStorage* storage) = 0; + virtual bool RunWithStorage(SettingsStorage* storage) = 0; // Sets error_ or result_ depending on the value of a storage ReadResult, and // returns whether the result implies success (i.e. !error). - bool UseReadResult( - const SettingsStorage::ReadResult& result); + bool UseReadResult(const SettingsStorage::ReadResult& result); // Sets error_ depending on the value of a storage WriteResult, sends a // change notification if needed, and returns whether the result implies // success (i.e. !error). - bool UseWriteResult( - scoped_refptr<SettingsObserverList> observers, - const SettingsStorage::WriteResult& result); + bool UseWriteResult(const SettingsStorage::WriteResult& result); private: // Called via PostTask from RunImpl. Calls RunWithStorage and then // SendReponse with its success value. - void RunWithStorageOnFileThread( - scoped_refptr<SettingsObserverList> observers, - SettingsStorage* storage); + void RunWithStorageOnFileThread(SettingsStorage* storage); + + // The settings namespace the call was for. For example, SYNC if the API + // call was chrome.settings.experimental.sync..., LOCAL if .local, etc. + settings_namespace::Namespace settings_namespace_; + + // Observers, cached so that it's only grabbed from the UI thread. + scoped_refptr<SettingsObserverList> observers_; }; class GetSettingsFunction : public SettingsFunction { @@ -53,9 +58,7 @@ class GetSettingsFunction : public SettingsFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.settings.get"); protected: - virtual bool RunWithStorage( - scoped_refptr<SettingsObserverList> observers, - SettingsStorage* storage) OVERRIDE; + virtual bool RunWithStorage(SettingsStorage* storage) OVERRIDE; }; class SetSettingsFunction : public SettingsFunction { @@ -63,9 +66,7 @@ class SetSettingsFunction : public SettingsFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.settings.set"); protected: - virtual bool RunWithStorage( - scoped_refptr<SettingsObserverList> observers, - SettingsStorage* storage) OVERRIDE; + virtual bool RunWithStorage(SettingsStorage* storage) OVERRIDE; virtual void GetQuotaLimitHeuristics( QuotaLimitHeuristics* heuristics) const OVERRIDE; @@ -76,9 +77,7 @@ class RemoveSettingsFunction : public SettingsFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.settings.remove"); protected: - virtual bool RunWithStorage( - scoped_refptr<SettingsObserverList> observers, - SettingsStorage* storage) OVERRIDE; + virtual bool RunWithStorage(SettingsStorage* storage) OVERRIDE; virtual void GetQuotaLimitHeuristics( QuotaLimitHeuristics* heuristics) const OVERRIDE; @@ -89,9 +88,7 @@ class ClearSettingsFunction : public SettingsFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.settings.clear"); protected: - virtual bool RunWithStorage( - scoped_refptr<SettingsObserverList> observers, - SettingsStorage* storage) OVERRIDE; + virtual bool RunWithStorage(SettingsStorage* storage) OVERRIDE; virtual void GetQuotaLimitHeuristics( QuotaLimitHeuristics* heuristics) const OVERRIDE; diff --git a/chrome/browser/extensions/settings/settings_apitest.cc b/chrome/browser/extensions/settings/settings_apitest.cc index 2d99f85..093930ee 100644 --- a/chrome/browser/extensions/settings/settings_apitest.cc +++ b/chrome/browser/extensions/settings/settings_apitest.cc @@ -8,6 +8,7 @@ #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/settings/settings_frontend.h" +#include "chrome/browser/extensions/settings/settings_namespace.h" #include "chrome/browser/extensions/settings/settings_sync_util.h" #include "chrome/browser/extensions/extension_test_message_listener.h" #include "chrome/browser/profiles/profile.h" @@ -19,6 +20,8 @@ namespace extensions { +using namespace settings_namespace; + namespace { class NoopSyncChangeProcessor : public SyncChangeProcessor { @@ -37,24 +40,32 @@ class NoopSyncChangeProcessor : public SyncChangeProcessor { class ExtensionSettingsApiTest : public ExtensionApiTest { protected: void ReplyWhenSatisfied( + Namespace settings_namespace, const std::string& normal_action, const std::string& incognito_action) { MaybeLoadAndReplyWhenSatisfied( - normal_action, incognito_action, NULL, false); + settings_namespace, normal_action, incognito_action, NULL, false); } const Extension* LoadAndReplyWhenSatisfied( + Namespace settings_namespace, const std::string& normal_action, const std::string& incognito_action, const std::string& extension_dir) { return MaybeLoadAndReplyWhenSatisfied( - normal_action, incognito_action, &extension_dir, false); + settings_namespace, + normal_action, + incognito_action, + &extension_dir, + false); } void FinalReplyWhenSatisfied( + Namespace settings_namespace, const std::string& normal_action, const std::string& incognito_action) { - MaybeLoadAndReplyWhenSatisfied(normal_action, incognito_action, NULL, true); + MaybeLoadAndReplyWhenSatisfied( + settings_namespace, normal_action, incognito_action, NULL, true); } void InitSync(SyncChangeProcessor* sync_processor) { @@ -83,6 +94,7 @@ class ExtensionSettingsApiTest : public ExtensionApiTest { private: const Extension* MaybeLoadAndReplyWhenSatisfied( + Namespace settings_namespace, const std::string& normal_action, const std::string& incognito_action, // May be NULL to imply not loading the extension. @@ -103,13 +115,19 @@ class ExtensionSettingsApiTest : public ExtensionApiTest { EXPECT_TRUE(listener.WaitUntilSatisfied()); EXPECT_TRUE(listener_incognito.WaitUntilSatisfied()); - listener.Reply(CreateMessage(normal_action, is_final_action)); - listener_incognito.Reply(CreateMessage(incognito_action, is_final_action)); + listener.Reply( + CreateMessage(settings_namespace, normal_action, is_final_action)); + listener_incognito.Reply( + CreateMessage(settings_namespace, incognito_action, is_final_action)); return extension; } - std::string CreateMessage(const std::string& action, bool is_final_action) { + std::string CreateMessage( + Namespace settings_namespace, + const std::string& action, + bool is_final_action) { scoped_ptr<DictionaryValue> message(new DictionaryValue()); + message->SetString("namespace", ToString(settings_namespace)); message->SetString("action", action); message->SetBoolean("isFinalAction", is_final_action); std::string message_json; @@ -153,15 +171,16 @@ IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, SplitModeIncognito) { catcher_incognito.RestrictToProfile( browser()->profile()->GetOffTheRecordProfile()); - LoadAndReplyWhenSatisfied("assertEmpty", "assertEmpty", "split_incognito"); - ReplyWhenSatisfied("noop", "setFoo"); - ReplyWhenSatisfied("assertFoo", "assertFoo"); - ReplyWhenSatisfied("clear", "noop"); - ReplyWhenSatisfied("assertEmpty", "assertEmpty"); - ReplyWhenSatisfied("setFoo", "noop"); - ReplyWhenSatisfied("assertFoo", "assertFoo"); - ReplyWhenSatisfied("noop", "removeFoo"); - FinalReplyWhenSatisfied("assertEmpty", "assertEmpty"); + LoadAndReplyWhenSatisfied(SYNC, + "assertEmpty", "assertEmpty", "split_incognito"); + ReplyWhenSatisfied(SYNC, "noop", "setFoo"); + ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); + ReplyWhenSatisfied(SYNC, "clear", "noop"); + ReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty"); + ReplyWhenSatisfied(SYNC, "setFoo", "noop"); + ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); + ReplyWhenSatisfied(SYNC, "noop", "removeFoo"); + FinalReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty"); EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); @@ -179,14 +198,68 @@ IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, catcher_incognito.RestrictToProfile( browser()->profile()->GetOffTheRecordProfile()); - LoadAndReplyWhenSatisfied( + LoadAndReplyWhenSatisfied(SYNC, + "assertNoNotifications", "assertNoNotifications", "split_incognito"); + ReplyWhenSatisfied(SYNC, "noop", "setFoo"); + ReplyWhenSatisfied(SYNC, + "assertAddFooNotification", "assertAddFooNotification"); + ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications"); + ReplyWhenSatisfied(SYNC, "removeFoo", "noop"); + FinalReplyWhenSatisfied(SYNC, + "assertDeleteFooNotification", "assertDeleteFooNotification"); + + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); + EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); +} + +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, + SyncAndLocalAreasAreSeparate) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + + // We need 2 ResultCatchers because we'll be running the same test in both + // regular and incognito mode. + ResultCatcher catcher, catcher_incognito; + catcher.RestrictToProfile(browser()->profile()); + catcher_incognito.RestrictToProfile( + browser()->profile()->GetOffTheRecordProfile()); + + LoadAndReplyWhenSatisfied(SYNC, "assertNoNotifications", "assertNoNotifications", "split_incognito"); - ReplyWhenSatisfied("noop", "setFoo"); - ReplyWhenSatisfied("assertAddFooNotification", "assertAddFooNotification"); - ReplyWhenSatisfied("clearNotifications", "clearNotifications"); - ReplyWhenSatisfied("removeFoo", "noop"); - FinalReplyWhenSatisfied( + + ReplyWhenSatisfied(SYNC, "noop", "setFoo"); + ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); + ReplyWhenSatisfied(SYNC, + "assertAddFooNotification", "assertAddFooNotification"); + ReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty"); + ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications"); + + ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications"); + + ReplyWhenSatisfied(LOCAL, "setFoo", "noop"); + ReplyWhenSatisfied(LOCAL, "assertFoo", "assertFoo"); + ReplyWhenSatisfied(LOCAL, + "assertAddFooNotification", "assertAddFooNotification"); + ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); + ReplyWhenSatisfied(SYNC, "assertNoNotifications", "assertNoNotifications"); + + ReplyWhenSatisfied(LOCAL, "clearNotifications", "clearNotifications"); + + ReplyWhenSatisfied(LOCAL, "noop", "removeFoo"); + ReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty"); + ReplyWhenSatisfied(LOCAL, "assertDeleteFooNotification", "assertDeleteFooNotification"); + ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo"); + ReplyWhenSatisfied(SYNC, "assertNoNotifications", "assertNoNotifications"); + + ReplyWhenSatisfied(LOCAL, "clearNotifications", "clearNotifications"); + + ReplyWhenSatisfied(SYNC, "removeFoo", "noop"); + ReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty"); + ReplyWhenSatisfied(SYNC, + "assertDeleteFooNotification", "assertDeleteFooNotification"); + ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications"); + FinalReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty"); EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); @@ -206,7 +279,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, browser()->profile()->GetOffTheRecordProfile()); const Extension* extension = - LoadAndReplyWhenSatisfied( + LoadAndReplyWhenSatisfied(SYNC, "assertNoNotifications", "assertNoNotifications", "split_incognito"); const std::string& extension_id = extension->id(); @@ -220,8 +293,9 @@ IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, extension_id, "foo", bar)); SendChanges(sync_changes); - ReplyWhenSatisfied("assertAddFooNotification", "assertAddFooNotification"); - ReplyWhenSatisfied("clearNotifications", "clearNotifications"); + ReplyWhenSatisfied(SYNC, + "assertAddFooNotification", "assertAddFooNotification"); + ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications"); // Remove "foo" via sync. sync_changes.clear(); @@ -229,11 +303,57 @@ IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, extension_id, "foo")); SendChanges(sync_changes); - FinalReplyWhenSatisfied( + FinalReplyWhenSatisfied(SYNC, "assertDeleteFooNotification", "assertDeleteFooNotification"); EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); } +// Disabled, see crbug.com/101110 +// +// TODO: boring test, already done in the unit tests. What we really should be +// be testing is that the areas don't overlap. +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, + DISABLED_OnChangedNotificationsFromSyncNotSentToLocal) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + + // We need 2 ResultCatchers because we'll be running the same test in both + // regular and incognito mode. + ResultCatcher catcher, catcher_incognito; + catcher.RestrictToProfile(browser()->profile()); + catcher_incognito.RestrictToProfile( + browser()->profile()->GetOffTheRecordProfile()); + + const Extension* extension = + LoadAndReplyWhenSatisfied(LOCAL, + "assertNoNotifications", "assertNoNotifications", "split_incognito"); + const std::string& extension_id = extension->id(); + + NoopSyncChangeProcessor sync_processor; + InitSync(&sync_processor); + + // Set "foo" to "bar" via sync. + SyncChangeList sync_changes; + StringValue bar("bar"); + sync_changes.push_back(settings_sync_util::CreateAdd( + extension_id, "foo", bar)); + SendChanges(sync_changes); + + ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications"); + + // Remove "foo" via sync. + sync_changes.clear(); + sync_changes.push_back(settings_sync_util::CreateDelete( + extension_id, "foo")); + SendChanges(sync_changes); + + FinalReplyWhenSatisfied(LOCAL, + "assertNoNotifications", "assertNoNotifications"); + + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); + EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); +} + } // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_backend.cc b/chrome/browser/extensions/settings/settings_backend.cc index 2a6fee5..e6fda7d 100644 --- a/chrome/browser/extensions/settings/settings_backend.cc +++ b/chrome/browser/extensions/settings/settings_backend.cc @@ -39,7 +39,7 @@ const size_t kMaxSettingKeys = 512; } // namespace SettingsBackend::SettingsBackend( - SettingsStorageFactory* storage_factory, + const scoped_refptr<SettingsStorageFactory>& storage_factory, const FilePath& base_path, const scoped_refptr<SettingsObserverList>& observers) : storage_factory_(storage_factory), @@ -61,8 +61,7 @@ SettingsStorage* SettingsBackend::GetStorage( return GetOrCreateStorageWithSyncData(extension_id, empty); } -SyncableSettingsStorage* -SettingsBackend::GetOrCreateStorageWithSyncData( +SyncableSettingsStorage* SettingsBackend::GetOrCreateStorageWithSyncData( const std::string& extension_id, const DictionaryValue& sync_data) const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); diff --git a/chrome/browser/extensions/settings/settings_backend.h b/chrome/browser/extensions/settings/settings_backend.h index c9365d8..ab36894 100644 --- a/chrome/browser/extensions/settings/settings_backend.h +++ b/chrome/browser/extensions/settings/settings_backend.h @@ -28,9 +28,8 @@ class SettingsBackend : public SyncableService { // |base_path| is the base of the extension settings directory, so the // databases will be at base_path/extension_id. // |observers| is the list of observers to settings changes. - explicit SettingsBackend( - // Ownership NOT taken. - SettingsStorageFactory* storage_factory, + SettingsBackend( + const scoped_refptr<SettingsStorageFactory>& storage_factory, const FilePath& base_path, const scoped_refptr<SettingsObserverList>& observers); @@ -70,8 +69,8 @@ class SettingsBackend : public SyncableService { // Disable the syncing of the storage area for |extension_id|. void DisableSyncForExtension(const std::string& extension_id) const; - // The Factory to use for creating leveldb storage areas. Not owned. - SettingsStorageFactory* const storage_factory_; + // The Factory to use for creating leveldb storage areas. + const scoped_refptr<SettingsStorageFactory> storage_factory_; // The base file path to create any leveldb databases at. const FilePath base_path_; diff --git a/chrome/browser/extensions/settings/settings_frontend.cc b/chrome/browser/extensions/settings/settings_frontend.cc index 1117e55..6ac79fd 100644 --- a/chrome/browser/extensions/settings/settings_frontend.cc +++ b/chrome/browser/extensions/settings/settings_frontend.cc @@ -10,68 +10,57 @@ #include "chrome/browser/extensions/extension_event_router.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/settings/settings_backend.h" +#include "chrome/browser/extensions/settings/settings_namespace.h" #include "chrome/browser/extensions/settings/settings_leveldb_storage.h" #include "chrome/browser/profiles/profile.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" -namespace extensions { - using content::BrowserThread; +namespace extensions { + namespace { -struct Backends { - Backends( - // Ownership taken. - SettingsStorageFactory* storage_factory, - const FilePath& profile_path, - const scoped_refptr<SettingsObserverList>& observers) - : storage_factory_(storage_factory), - extensions_backend_( - storage_factory, - profile_path.AppendASCII( - ExtensionService::kExtensionSettingsDirectoryName), - observers), - apps_backend_( - storage_factory, - profile_path.AppendASCII( - ExtensionService::kAppSettingsDirectoryName), - observers) {} +// Settings change Observer which forwards changes on to the extension +// processes for |profile| and its incognito partner if it exists. +class DefaultObserver : public SettingsObserver { + public: + explicit DefaultObserver(Profile* profile) : profile_(profile) {} + + // SettingsObserver implementation. + virtual void OnSettingsChanged( + const std::string& extension_id, + settings_namespace::Namespace settings_namespace, + const std::string& change_json) OVERRIDE { + profile_->GetExtensionEventRouter()->DispatchEventToExtension( + extension_id, + extension_event_names::kOnSettingsChanged, + // This is the list of function arguments to pass to the onChanged + // handler of extensions, an array of [changes, settings_namespace]. + std::string("[") + change_json + ",\"" + + settings_namespace::ToString(settings_namespace) + "\"]", + NULL, + GURL()); + } - scoped_ptr<SettingsStorageFactory> storage_factory_; - SettingsBackend extensions_backend_; - SettingsBackend apps_backend_; + private: + Profile* const profile_; }; -static void CallbackWithExtensionsBackend( +void CallbackWithSyncableService( const SettingsFrontend::SyncableServiceCallback& callback, - Backends* backends) { + SettingsBackend* backend) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - callback.Run(&backends->extensions_backend_); + callback.Run(backend); } -void CallbackWithAppsBackend( - const SettingsFrontend::SyncableServiceCallback& callback, - Backends* backends) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - callback.Run(&backends->apps_backend_); -} - -void CallbackWithExtensionsStorage( +void CallbackWithStorage( const std::string& extension_id, const SettingsFrontend::StorageCallback& callback, - Backends* backends) { + SettingsBackend* backend) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - callback.Run(backends->extensions_backend_.GetStorage(extension_id)); -} - -void CallbackWithAppsStorage( - const std::string& extension_id, - const SettingsFrontend::StorageCallback& callback, - Backends* backends) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - callback.Run(backends->apps_backend_.GetStorage(extension_id)); + callback.Run(backend->GetStorage(extension_id)); } void CallbackWithNullStorage( @@ -81,103 +70,94 @@ void CallbackWithNullStorage( } void DeleteStorageOnFileThread( - const std::string& extension_id, Backends* backends) { + const std::string& extension_id, SettingsBackend* backend) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - backends->extensions_backend_.DeleteStorage(extension_id); - backends->apps_backend_.DeleteStorage(extension_id); + backend->DeleteStorage(extension_id); } } // namespace -// DefaultObserver - -SettingsFrontend::DefaultObserver::DefaultObserver(Profile* profile) - : profile_(profile) {} - -SettingsFrontend::DefaultObserver::~DefaultObserver() {} - -void SettingsFrontend::DefaultObserver::OnSettingsChanged( - const std::string& extension_id, const std::string& changes_json) { - profile_->GetExtensionEventRouter()->DispatchEventToExtension( - extension_id, - extension_event_names::kOnSettingsChanged, - // This is the list of function arguments to pass to the onChanged - // handler of extensions, a single argument with the list of changes. - std::string("[") + changes_json + "]", - NULL, - GURL()); -} - -// Core - -class SettingsFrontend::Core - : public base::RefCountedThreadSafe<Core> { +// Ref-counted container for a SettingsBackend object. +class SettingsFrontend::BackendWrapper + : public base::RefCountedThreadSafe<BackendWrapper> { public: - explicit Core( - // Ownership taken. - SettingsStorageFactory* storage_factory, - const scoped_refptr<SettingsObserverList>& observers) - : storage_factory_(storage_factory), - observers_(observers), - backends_(NULL) { + // Creates a new BackendWrapper and initializes it on the FILE thread. + static scoped_refptr<BackendWrapper> CreateAndInit( + const scoped_refptr<SettingsStorageFactory>& factory, + const scoped_refptr<SettingsObserverList>& observers, + const FilePath& path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + scoped_refptr<BackendWrapper> backend_wrapper = + new BackendWrapper(factory, observers); + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + base::Bind( + &SettingsFrontend::BackendWrapper::InitOnFileThread, + backend_wrapper, + path)); + return backend_wrapper; } - typedef base::Callback<void(Backends*)> BackendsCallback; - - // Does any FILE thread specific initialization, such as construction of - // |backend_|. Must be called before any call to - // RunWithBackendOnFileThread(). - void InitOnFileThread(const FilePath& profile_path) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DCHECK(!backends_); - backends_ = - new Backends( - storage_factory_.release(), profile_path, observers_); - } + typedef base::Callback<void(SettingsBackend*)> BackendCallback; - // Runs |callback| with both the extensions and apps settings on the FILE - // thread. - void RunWithBackends(const BackendsCallback& callback) { + // Runs |callback| with the wrapped Backend on the FILE thread. + void RunWithBackend(const BackendCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, base::Bind( - &SettingsFrontend::Core::RunWithBackendsOnFileThread, + &SettingsFrontend::BackendWrapper::RunWithBackendOnFileThread, this, callback)); } private: - void RunWithBackendsOnFileThread(const BackendsCallback& callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - DCHECK(backends_); - callback.Run(backends_); + friend class base::RefCountedThreadSafe<BackendWrapper>; + + BackendWrapper( + const scoped_refptr<SettingsStorageFactory>& storage_factory, + const scoped_refptr<SettingsObserverList>& observers) + : storage_factory_(storage_factory), + observers_(observers), + backend_(NULL) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } - virtual ~Core() { + virtual ~BackendWrapper() { if (BrowserThread::CurrentlyOn(BrowserThread::FILE)) { - delete backends_; + delete backend_; } else if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, backends_); + BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, backend_); } else { NOTREACHED(); } } - friend class base::RefCountedThreadSafe<Core>; + void InitOnFileThread(const FilePath& path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(!backend_); + backend_ = new SettingsBackend(storage_factory_, path, observers_); + storage_factory_ = NULL; + observers_ = NULL; + } - // Leveldb storage area factory. Ownership passed to Backends on Init. - scoped_ptr<SettingsStorageFactory> storage_factory_; + void RunWithBackendOnFileThread(const BackendCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + DCHECK(backend_); + callback.Run(backend_); + } - // Observers to settings changes (thread safe). + // Only need these until |backend_| exists. + scoped_refptr<SettingsStorageFactory> storage_factory_; scoped_refptr<SettingsObserverList> observers_; - // Backends for extensions and apps settings. Lives on FILE thread. - Backends* backends_; + // Wrapped Backend. Used exclusively on the FILE thread, and is created on + // the FILE thread in InitOnFileThread. + SettingsBackend* backend_; - DISALLOW_COPY_AND_ASSIGN(Core); + DISALLOW_COPY_AND_ASSIGN(BackendWrapper); }; // SettingsFrontend @@ -189,54 +169,75 @@ SettingsFrontend* SettingsFrontend::Create(Profile* profile) { /* static */ SettingsFrontend* SettingsFrontend::Create( - SettingsStorageFactory* storage_factory, Profile* profile) { + const scoped_refptr<SettingsStorageFactory>& storage_factory, + Profile* profile) { return new SettingsFrontend(storage_factory, profile); } SettingsFrontend::SettingsFrontend( - SettingsStorageFactory* storage_factory, Profile* profile) + const scoped_refptr<SettingsStorageFactory>& factory, Profile* profile) : profile_(profile), observers_(new SettingsObserverList()), - default_observer_(profile), - core_(new SettingsFrontend::Core(storage_factory, observers_)) { + profile_observer_(new DefaultObserver(profile)) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(!profile->IsOffTheRecord()); - observers_->AddObserver(&default_observer_); + observers_->AddObserver(profile_observer_.get()); - BrowserThread::PostTask( - BrowserThread::FILE, - FROM_HERE, - base::Bind( - &SettingsFrontend::Core::InitOnFileThread, - core_.get(), - profile->GetPath())); + const FilePath& profile_path = profile->GetPath(); + backends_[settings_namespace::LOCAL].app = + BackendWrapper::CreateAndInit( + factory, + observers_, + profile_path.AppendASCII( + ExtensionService::kLocalAppSettingsDirectoryName)); + backends_[settings_namespace::LOCAL].extension = + BackendWrapper::CreateAndInit( + factory, + observers_, + profile_path.AppendASCII( + ExtensionService::kLocalExtensionSettingsDirectoryName)); + backends_[settings_namespace::SYNC].app = + BackendWrapper::CreateAndInit( + factory, + observers_, + profile_path.AppendASCII( + ExtensionService::kSyncAppSettingsDirectoryName)); + backends_[settings_namespace::SYNC].extension = + BackendWrapper::CreateAndInit( + factory, + observers_, + profile_path.AppendASCII( + ExtensionService::kSyncExtensionSettingsDirectoryName)); } SettingsFrontend::~SettingsFrontend() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - observers_->RemoveObserver(&default_observer_); + observers_->RemoveObserver(profile_observer_.get()); } void SettingsFrontend::RunWithSyncableService( syncable::ModelType model_type, const SyncableServiceCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + scoped_refptr<BackendWrapper> backend; switch (model_type) { - case syncable::EXTENSION_SETTINGS: - core_->RunWithBackends( - base::Bind(&CallbackWithExtensionsBackend, callback)); - break; case syncable::APP_SETTINGS: - core_->RunWithBackends( - base::Bind(&CallbackWithAppsBackend, callback)); + backend = backends_[settings_namespace::SYNC].app; break; + + case syncable::EXTENSION_SETTINGS: + backend = backends_[settings_namespace::SYNC].extension; + break; + default: NOTREACHED(); } + backend->RunWithBackend(base::Bind(&CallbackWithSyncableService, callback)); } void SettingsFrontend::RunWithStorage( const std::string& extension_id, + settings_namespace::Namespace settings_namespace, const StorageCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -250,25 +251,36 @@ void SettingsFrontend::RunWithStorage( return; } + scoped_refptr<BackendWrapper> backend; if (extension->is_app()) { - core_->RunWithBackends( - base::Bind(&CallbackWithAppsStorage, extension_id, callback)); + backend = backends_[settings_namespace].app; } else { - core_->RunWithBackends( - base::Bind(&CallbackWithExtensionsStorage, extension_id, callback)); + backend = backends_[settings_namespace].extension; } + backend->RunWithBackend( + base::Bind(&CallbackWithStorage, extension_id, callback)); } void SettingsFrontend::DeleteStorageSoon( const std::string& extension_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - core_->RunWithBackends(base::Bind(&DeleteStorageOnFileThread, extension_id)); + SettingsFrontend::BackendWrapper::BackendCallback callback = + base::Bind(&DeleteStorageOnFileThread, extension_id); + for (std::map<settings_namespace::Namespace, BackendWrappers>::iterator it = + backends_.begin(); it != backends_.end(); ++it) { + it->second.app->RunWithBackend(callback); + it->second.extension->RunWithBackend(callback); + } } -scoped_refptr<SettingsObserverList> -SettingsFrontend::GetObservers() { +scoped_refptr<SettingsObserverList> SettingsFrontend::GetObservers() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); return observers_; } +// BackendWrappers + +SettingsFrontend::BackendWrappers::BackendWrappers() {} +SettingsFrontend::BackendWrappers::~BackendWrappers() {} + } // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_frontend.h b/chrome/browser/extensions/settings/settings_frontend.h index 66cb488..f30792a 100644 --- a/chrome/browser/extensions/settings/settings_frontend.h +++ b/chrome/browser/extensions/settings/settings_frontend.h @@ -14,6 +14,7 @@ #include "base/memory/scoped_ptr.h" #include "base/observer_list_threadsafe.h" #include "chrome/browser/extensions/settings/settings_leveldb_storage.h" +#include "chrome/browser/extensions/settings/settings_namespace.h" #include "chrome/browser/extensions/settings/settings_observer.h" #include "chrome/browser/sync/api/syncable_service.h" #include "content/public/browser/notification_observer.h" @@ -34,8 +35,7 @@ class SettingsFrontend { static SettingsFrontend* Create(Profile* profile); static SettingsFrontend* Create( - // Ownership taken. - SettingsStorageFactory* storage_factory, + const scoped_refptr<SettingsStorageFactory>& storage_factory, // Owership NOT taken. Profile* profile); @@ -53,7 +53,9 @@ class SettingsFrontend { // |extension_id|. If there is no extension with that ID, the storage area // will be NULL. void RunWithStorage( - const std::string& extension_id, const StorageCallback& callback); + const std::string& extension_id, + settings_namespace::Namespace settings_namespace, + const StorageCallback& callback); // Deletes the settings for an extension (on the FILE thread). void DeleteStorageSoon(const std::string& extension_id); @@ -62,25 +64,8 @@ class SettingsFrontend { scoped_refptr<SettingsObserverList> GetObservers(); private: - // Settings change Observer which forwards changes on to the extension - // processes for |profile| and its incognito partner if it exists. - class DefaultObserver : public SettingsObserver { - public: - explicit DefaultObserver(Profile* profile); - virtual ~DefaultObserver(); - - // SettingsObserver implementation. - virtual void OnSettingsChanged( - const std::string& extension_id, - const std::string& change_json) OVERRIDE; - - private: - Profile* const profile_; - }; - SettingsFrontend( - // Ownership taken. - SettingsStorageFactory* storage_factory, + const scoped_refptr<SettingsStorageFactory>& storage_factory, // Ownership NOT taken. Profile* profile); @@ -91,11 +76,21 @@ class SettingsFrontend { scoped_refptr<SettingsObserverList> observers_; // Observer for |profile_|. - DefaultObserver default_observer_; - - // Ref-counted container for the SettingsBackend object. - class Core; - scoped_refptr<Core> core_; + scoped_ptr<SettingsObserver> profile_observer_; + + // Ref-counted container for each SettingsBackend object. There are 4: an + // apps and an extensions Backend for the LOCAL namespace, and likewise for + // the SYNC namespace. They only differ in what directory the database for + // each exists in (and the Backends in the SYNC namespace happen to be + // returned from RunWithSyncableService). + class BackendWrapper; + struct BackendWrappers { + BackendWrappers(); + ~BackendWrappers(); + scoped_refptr<BackendWrapper> app; + scoped_refptr<BackendWrapper> extension; + }; + std::map<settings_namespace::Namespace, BackendWrappers> backends_; DISALLOW_COPY_AND_ASSIGN(SettingsFrontend); }; diff --git a/chrome/browser/extensions/settings/settings_frontend_unittest.cc b/chrome/browser/extensions/settings/settings_frontend_unittest.cc index cbebf43..136f578 100644 --- a/chrome/browser/extensions/settings/settings_frontend_unittest.cc +++ b/chrome/browser/extensions/settings/settings_frontend_unittest.cc @@ -29,13 +29,15 @@ const SettingsStorage::WriteOptions DEFAULTS = SettingsStorage::DEFAULTS; // A SettingsStorageFactory which always returns NULL. class NullSettingsStorageFactory : public SettingsStorageFactory { public: - virtual ~NullSettingsStorageFactory() {} - // SettingsStorageFactory implementation. virtual SettingsStorage* Create( const FilePath& base_path, const std::string& extension_id) OVERRIDE { return NULL; } + + private: + // SettingsStorageFactory is refcounted. + virtual ~NullSettingsStorageFactory() {} }; } @@ -43,7 +45,8 @@ class NullSettingsStorageFactory : public SettingsStorageFactory { class ExtensionSettingsFrontendTest : public testing::Test { public: ExtensionSettingsFrontendTest() - : ui_thread_(BrowserThread::UI, MessageLoop::current()), + : storage_factory_(new ScopedSettingsStorageFactory()), + ui_thread_(BrowserThread::UI, MessageLoop::current()), file_thread_(BrowserThread::FILE, MessageLoop::current()) {} virtual void SetUp() OVERRIDE { @@ -59,17 +62,15 @@ class ExtensionSettingsFrontendTest : public testing::Test { protected: void ResetFrontend() { - storage_factory_ = - new ScopedSettingsStorageFactory(new SettingsLeveldbStorage::Factory()); - frontend_.reset(SettingsFrontend::Create(storage_factory_, profile_.get())); + storage_factory_->Reset(new SettingsLeveldbStorage::Factory()); + frontend_.reset( + SettingsFrontend::Create(storage_factory_.get(), profile_.get())); } ScopedTempDir temp_dir_; scoped_ptr<MockProfile> profile_; scoped_ptr<SettingsFrontend> frontend_; - - // Owned by |frontend_|. - ScopedSettingsStorageFactory* storage_factory_; + scoped_refptr<ScopedSettingsStorageFactory> storage_factory_; private: MessageLoop message_loop_; diff --git a/chrome/browser/extensions/settings/settings_leveldb_storage.h b/chrome/browser/extensions/settings/settings_leveldb_storage.h index 96cff05..390bc61 100644 --- a/chrome/browser/extensions/settings/settings_leveldb_storage.h +++ b/chrome/browser/extensions/settings/settings_leveldb_storage.h @@ -29,15 +29,13 @@ class SettingsLeveldbStorage : public SettingsStorage { // Factory for creating SettingsLeveldbStorage instances. class Factory : public SettingsStorageFactory { public: - Factory() {} - virtual ~Factory() {} - - // SettingsStorageFactory implementation. virtual SettingsStorage* Create( - const FilePath& base_path, const std::string& extension_id) OVERRIDE; + const FilePath& base_path, + const std::string& extension_id) OVERRIDE; private: - DISALLOW_COPY_AND_ASSIGN(Factory); + // SettingsStorageFactory is refcounted. + virtual ~Factory() {} }; // Must be deleted on the FILE thread. diff --git a/chrome/browser/extensions/settings/settings_leveldb_storage_unittest.cc b/chrome/browser/extensions/settings/settings_leveldb_storage_unittest.cc index 8e7ec39..6d8aa10 100644 --- a/chrome/browser/extensions/settings/settings_leveldb_storage_unittest.cc +++ b/chrome/browser/extensions/settings/settings_leveldb_storage_unittest.cc @@ -4,6 +4,7 @@ #include "chrome/browser/extensions/settings/settings_storage_unittest.h" +#include "base/memory/ref_counted.h" #include "chrome/browser/extensions/settings/settings_leveldb_storage.h" namespace extensions { @@ -12,7 +13,8 @@ namespace { SettingsStorage* Param( const FilePath& file_path, const std::string& extension_id) { - return SettingsLeveldbStorage::Factory().Create(file_path, extension_id); + return scoped_refptr<SettingsStorageFactory>( + new SettingsLeveldbStorage::Factory())->Create(file_path, extension_id); } } // namespace diff --git a/chrome/browser/extensions/settings/settings_namespace.cc b/chrome/browser/extensions/settings/settings_namespace.cc new file mode 100644 index 0000000..7340592 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_namespace.cc @@ -0,0 +1,38 @@ +// 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/settings/settings_namespace.h" + +#include "base/logging.h" + +namespace extensions { + +namespace settings_namespace { + +namespace { +const char* kLocalNamespace = "local"; +const char* kSyncNamespace = "sync"; +} // namespace + +std::string ToString(Namespace settings_namespace) { + switch (settings_namespace) { + case LOCAL: return kLocalNamespace; + case SYNC: return kSyncNamespace; + case INVALID: + default: NOTREACHED(); + } + return std::string(); +} + +Namespace FromString(const std::string& namespace_string) { + if (namespace_string == kLocalNamespace) + return LOCAL; + else if (namespace_string == kSyncNamespace) + return SYNC; + return INVALID; +} + +} // namespace settings_namespace + +} // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_namespace.h b/chrome/browser/extensions/settings/settings_namespace.h new file mode 100644 index 0000000..5062009 --- /dev/null +++ b/chrome/browser/extensions/settings/settings_namespace.h @@ -0,0 +1,34 @@ +// 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_SETTINGS_SETTINGS_NAMESPACE_H_ +#define CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_NAMESPACE_H_ +#pragma once + +#include <string> + +namespace extensions { + +namespace settings_namespace { + +// The namespaces of the storage areas. +enum Namespace { + LOCAL, // "local" i.e. chrome.experimental.settings.local + SYNC, // "sync" i.e. chrome.experimental.settings.sync + INVALID +}; + +// Converts a namespace to its string representation. +// Namespace must not be INVALID. +std::string ToString(Namespace settings_namespace); + +// Converts a string representation of a namespace to its namespace, or INVALID +// if the string doesn't map to one. +Namespace FromString(const std::string& ns_string); + +} // namespace settings_namespace + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_NAMESPACE_H_ diff --git a/chrome/browser/extensions/settings/settings_observer.cc b/chrome/browser/extensions/settings/settings_observer.cc deleted file mode 100644 index 2ac2a85..0000000 --- a/chrome/browser/extensions/settings/settings_observer.cc +++ /dev/null @@ -1,11 +0,0 @@ -// 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/settings/settings_observer.h" - -namespace extensions { - -SettingsObserver::~SettingsObserver() {} - -} // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_observer.h b/chrome/browser/extensions/settings/settings_observer.h index ca4ebd6..1d8b4c0 100644 --- a/chrome/browser/extensions/settings/settings_observer.h +++ b/chrome/browser/extensions/settings/settings_observer.h @@ -7,6 +7,7 @@ #pragma once #include "base/observer_list_threadsafe.h" +#include "chrome/browser/extensions/settings/settings_namespace.h" namespace extensions { @@ -16,10 +17,10 @@ class SettingsObserver { // Called when a list of settings have changed for an extension. virtual void OnSettingsChanged( const std::string& extension_id, + settings_namespace::Namespace settings_namespace, const std::string& changes_json) = 0; - protected: - virtual ~SettingsObserver(); + virtual ~SettingsObserver() {} }; typedef ObserverListThreadSafe<SettingsObserver> diff --git a/chrome/browser/extensions/settings/settings_storage_factory.h b/chrome/browser/extensions/settings/settings_storage_factory.h index b39f45f..411d96e 100644 --- a/chrome/browser/extensions/settings/settings_storage_factory.h +++ b/chrome/browser/extensions/settings/settings_storage_factory.h @@ -6,21 +6,30 @@ #define CHROME_BROWSER_EXTENSIONS_SETTINGS_SETTINGS_STORAGE_FACTORY_H_ #pragma once -class FilePath; +#include <string> + +#include "base/file_path.h" +#include "base/memory/ref_counted.h" namespace extensions { class SettingsStorage; // Factory for creating SettingStorage instances. -class SettingsStorageFactory { +// +// Refcouted because it's just too messy to distribute these objects between +// SettingsBackend instances any other way. +class SettingsStorageFactory + : public base::RefCountedThreadSafe<SettingsStorageFactory> { public: - virtual ~SettingsStorageFactory() {} - - // Create a new SettingsLeveldbStorage area. Return NULL to indicate - // failure. Must be called on the FILE thread. + // Creates a new SettingsStorage area for an extension under |base_path|. + // Return NULL to indicate failure. Must be called on the FILE thread. virtual SettingsStorage* Create( const FilePath& base_path, const std::string& extension_id) = 0; + + protected: + friend class base::RefCountedThreadSafe<SettingsStorageFactory>; + virtual ~SettingsStorageFactory() {} }; } // namespace extensions diff --git a/chrome/browser/extensions/settings/settings_sync_unittest.cc b/chrome/browser/extensions/settings/settings_sync_unittest.cc index 1a2a428..ef84c79 100644 --- a/chrome/browser/extensions/settings/settings_sync_unittest.cc +++ b/chrome/browser/extensions/settings/settings_sync_unittest.cc @@ -162,6 +162,9 @@ class TestingSettingsStorageFactory : public SettingsStorageFactory { } private: + // SettingsStorageFactory is refcounted. + virtual ~TestingSettingsStorageFactory() {} + // None of these storage areas are owned by this factory, so care must be // taken when calling GetExisting. std::map<std::string, TestingSettingsStorage*> created_; @@ -177,14 +180,15 @@ class ExtensionSettingsSyncTest : public testing::Test { public: ExtensionSettingsSyncTest() : ui_thread_(BrowserThread::UI, MessageLoop::current()), - file_thread_(BrowserThread::FILE, MessageLoop::current()) {} + file_thread_(BrowserThread::FILE, MessageLoop::current()), + storage_factory_(new ScopedSettingsStorageFactory()) {} virtual void SetUp() OVERRIDE { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); profile_.reset(new MockProfile(temp_dir_.path())); - storage_factory_ = - new ScopedSettingsStorageFactory(new SettingsLeveldbStorage::Factory()); - frontend_.reset(SettingsFrontend::Create(storage_factory_, profile_.get())); + storage_factory_->Reset(new SettingsLeveldbStorage::Factory()); + frontend_.reset( + SettingsFrontend::Create(storage_factory_.get(), profile_.get())); } virtual void TearDown() OVERRIDE { @@ -234,9 +238,7 @@ class ExtensionSettingsSyncTest : public testing::Test { MockSyncChangeProcessor sync_; scoped_ptr<MockProfile> profile_; scoped_ptr<SettingsFrontend> frontend_; - - // Owned by |frontend_|. - ScopedSettingsStorageFactory* storage_factory_; + scoped_refptr<ScopedSettingsStorageFactory> storage_factory_; }; // Get a semblance of coverage for both EXTENSION_SETTINGS and APP_SETTINGS @@ -246,22 +248,22 @@ TEST_F(ExtensionSettingsSyncTest, NoDataDoesNotInvokeSync) { syncable::ModelType model_type = syncable::EXTENSION_SETTINGS; Extension::Type type = Extension::TYPE_EXTENSION; - ASSERT_EQ(0u, GetAllSyncData(model_type).size()); + EXPECT_EQ(0u, GetAllSyncData(model_type).size()); // Have one extension created before sync is set up, the other created after. AddExtensionAndGetStorage("s1", type); - ASSERT_EQ(0u, GetAllSyncData(model_type).size()); + EXPECT_EQ(0u, GetAllSyncData(model_type).size()); GetSyncableService(model_type)->MergeDataAndStartSyncing( model_type, SyncDataList(), &sync_); AddExtensionAndGetStorage("s2", type); - ASSERT_EQ(0u, GetAllSyncData(model_type).size()); + EXPECT_EQ(0u, GetAllSyncData(model_type).size()); GetSyncableService(model_type)->StopSyncing(model_type); - ASSERT_EQ(0u, sync_.changes().size()); - ASSERT_EQ(0u, GetAllSyncData(model_type).size()); + EXPECT_EQ(0u, sync_.changes().size()); + EXPECT_EQ(0u, GetAllSyncData(model_type).size()); } TEST_F(ExtensionSettingsSyncTest, InSyncDataDoesNotInvokeSync) { @@ -280,11 +282,11 @@ TEST_F(ExtensionSettingsSyncTest, InSyncDataDoesNotInvokeSync) { std::map<std::string, SettingSyncDataList> all_sync_data = GetAllSyncData(model_type); - 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()); + EXPECT_EQ(2u, all_sync_data.size()); + EXPECT_EQ(1u, all_sync_data["s1"].size()); + EXPECT_PRED_FORMAT2(ValuesEq, &value1, &all_sync_data["s1"][0].value()); + EXPECT_EQ(1u, all_sync_data["s2"].size()); + EXPECT_PRED_FORMAT2(ValuesEq, &value2, &all_sync_data["s2"][0].value()); SyncDataList sync_data; sync_data.push_back(settings_sync_util::CreateData( @@ -296,18 +298,18 @@ TEST_F(ExtensionSettingsSyncTest, InSyncDataDoesNotInvokeSync) { model_type, sync_data, &sync_); // Already in sync, so no changes. - ASSERT_EQ(0u, sync_.changes().size()); + EXPECT_EQ(0u, sync_.changes().size()); // Regression test: not-changing the synced value shouldn't result in a sync // change, and changing the synced value should result in an update. storage1->Set(DEFAULTS, "foo", value1); - ASSERT_EQ(0u, sync_.changes().size()); + EXPECT_EQ(0u, sync_.changes().size()); storage1->Set(DEFAULTS, "foo", value2); - ASSERT_EQ(1u, sync_.changes().size()); + EXPECT_EQ(1u, sync_.changes().size()); SettingSyncData change = sync_.GetOnlyChange("s1", "foo"); - ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type()); - ASSERT_TRUE(value2.Equals(&change.value())); + EXPECT_EQ(SyncChange::ACTION_UPDATE, change.change_type()); + EXPECT_TRUE(value2.Equals(&change.value())); GetSyncableService(model_type)->StopSyncing(model_type); } @@ -330,13 +332,13 @@ TEST_F(ExtensionSettingsSyncTest, LocalDataWithNoSyncDataIsPushedToSync) { model_type, SyncDataList(), &sync_); // All settings should have been pushed to sync. - ASSERT_EQ(2u, sync_.changes().size()); + EXPECT_EQ(2u, sync_.changes().size()); SettingSyncData change = sync_.GetOnlyChange("s1", "foo"); - ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type()); - ASSERT_TRUE(value1.Equals(&change.value())); + EXPECT_EQ(SyncChange::ACTION_ADD, change.change_type()); + EXPECT_TRUE(value1.Equals(&change.value())); change = sync_.GetOnlyChange("s2", "bar"); - ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type()); - ASSERT_TRUE(value2.Equals(&change.value())); + EXPECT_EQ(SyncChange::ACTION_ADD, change.change_type()); + EXPECT_TRUE(value2.Equals(&change.value())); GetSyncableService(model_type)->StopSyncing(model_type); } @@ -370,11 +372,11 @@ TEST_F(ExtensionSettingsSyncTest, AnySyncDataOverwritesLocalData) { SettingsStorage* storage2 = AddExtensionAndGetStorage("s2", type); // All changes should be local, so no sync changes. - ASSERT_EQ(0u, sync_.changes().size()); + EXPECT_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()); + EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); + EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); GetSyncableService(model_type)->StopSyncing(model_type); } @@ -416,8 +418,8 @@ TEST_F(ExtensionSettingsSyncTest, ProcessSyncChanges) { expected1.Set("bar", value2.DeepCopy()); expected2.Set("foo", value1.DeepCopy()); - ASSERT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); - ASSERT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); + EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); + EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); // Make sync update some settings, storage1 the new setting, storage2 the // initial setting. @@ -430,8 +432,8 @@ TEST_F(ExtensionSettingsSyncTest, ProcessSyncChanges) { expected1.Set("bar", value2.DeepCopy()); expected2.Set("bar", value1.DeepCopy()); - ASSERT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); - ASSERT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); + EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); + EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); // Make sync remove some settings, storage1 the initial setting, storage2 the // new setting. @@ -444,8 +446,8 @@ TEST_F(ExtensionSettingsSyncTest, ProcessSyncChanges) { expected1.Remove("foo", NULL); expected2.Remove("foo", NULL); - ASSERT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); - ASSERT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); + EXPECT_PRED_FORMAT2(SettingsEq, expected1, storage1->Get()); + EXPECT_PRED_FORMAT2(SettingsEq, expected2, storage2->Get()); GetSyncableService(model_type)->StopSyncing(model_type); } @@ -484,17 +486,17 @@ TEST_F(ExtensionSettingsSyncTest, PushToSync) { storage4->Set(DEFAULTS, "foo", value1); SettingSyncData change = sync_.GetOnlyChange("s1", "bar"); - ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type()); - ASSERT_TRUE(value2.Equals(&change.value())); + EXPECT_EQ(SyncChange::ACTION_ADD, change.change_type()); + EXPECT_TRUE(value2.Equals(&change.value())); sync_.GetOnlyChange("s2", "bar"); - ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type()); - ASSERT_TRUE(value2.Equals(&change.value())); + EXPECT_EQ(SyncChange::ACTION_ADD, change.change_type()); + EXPECT_TRUE(value2.Equals(&change.value())); change = sync_.GetOnlyChange("s3", "foo"); - ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type()); - ASSERT_TRUE(value1.Equals(&change.value())); + EXPECT_EQ(SyncChange::ACTION_ADD, change.change_type()); + EXPECT_TRUE(value1.Equals(&change.value())); change = sync_.GetOnlyChange("s4", "foo"); - ASSERT_EQ(SyncChange::ACTION_ADD, change.change_type()); - ASSERT_TRUE(value1.Equals(&change.value())); + EXPECT_EQ(SyncChange::ACTION_ADD, change.change_type()); + EXPECT_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 @@ -506,17 +508,17 @@ TEST_F(ExtensionSettingsSyncTest, PushToSync) { storage4->Set(DEFAULTS, "foo", value2); change = sync_.GetOnlyChange("s1", "bar"); - ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type()); - ASSERT_TRUE(value1.Equals(&change.value())); + EXPECT_EQ(SyncChange::ACTION_UPDATE, change.change_type()); + EXPECT_TRUE(value1.Equals(&change.value())); change = sync_.GetOnlyChange("s2", "foo"); - ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type()); - ASSERT_TRUE(value2.Equals(&change.value())); + EXPECT_EQ(SyncChange::ACTION_UPDATE, change.change_type()); + EXPECT_TRUE(value2.Equals(&change.value())); change = sync_.GetOnlyChange("s3", "bar"); - ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type()); - ASSERT_TRUE(value1.Equals(&change.value())); + EXPECT_EQ(SyncChange::ACTION_UPDATE, change.change_type()); + EXPECT_TRUE(value1.Equals(&change.value())); change = sync_.GetOnlyChange("s4", "foo"); - ASSERT_EQ(SyncChange::ACTION_UPDATE, change.change_type()); - ASSERT_TRUE(value2.Equals(&change.value())); + EXPECT_EQ(SyncChange::ACTION_UPDATE, change.change_type()); + EXPECT_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 @@ -527,16 +529,16 @@ TEST_F(ExtensionSettingsSyncTest, PushToSync) { storage3->Remove("foo"); storage4->Remove("bar"); - ASSERT_EQ( + EXPECT_EQ( SyncChange::ACTION_DELETE, sync_.GetOnlyChange("s1", "foo").change_type()); - ASSERT_EQ( + EXPECT_EQ( SyncChange::ACTION_DELETE, sync_.GetOnlyChange("s2", "bar").change_type()); - ASSERT_EQ( + EXPECT_EQ( SyncChange::ACTION_DELETE, sync_.GetOnlyChange("s3", "foo").change_type()); - ASSERT_EQ( + EXPECT_EQ( SyncChange::ACTION_DELETE, sync_.GetOnlyChange("s4", "bar").change_type()); @@ -547,7 +549,7 @@ TEST_F(ExtensionSettingsSyncTest, PushToSync) { storage3->Remove("foo"); storage4->Remove("bar"); - ASSERT_EQ(0u, sync_.changes().size()); + EXPECT_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. @@ -562,28 +564,28 @@ TEST_F(ExtensionSettingsSyncTest, PushToSync) { storage3->Clear(); storage4->Clear(); - ASSERT_EQ( + EXPECT_EQ( SyncChange::ACTION_DELETE, sync_.GetOnlyChange("s1", "foo").change_type()); - ASSERT_EQ( + EXPECT_EQ( SyncChange::ACTION_DELETE, sync_.GetOnlyChange("s1", "bar").change_type()); - ASSERT_EQ( + EXPECT_EQ( SyncChange::ACTION_DELETE, sync_.GetOnlyChange("s2", "foo").change_type()); - ASSERT_EQ( + EXPECT_EQ( SyncChange::ACTION_DELETE, sync_.GetOnlyChange("s2", "bar").change_type()); - ASSERT_EQ( + EXPECT_EQ( SyncChange::ACTION_DELETE, sync_.GetOnlyChange("s3", "foo").change_type()); - ASSERT_EQ( + EXPECT_EQ( SyncChange::ACTION_DELETE, sync_.GetOnlyChange("s3", "bar").change_type()); - ASSERT_EQ( + EXPECT_EQ( SyncChange::ACTION_DELETE, sync_.GetOnlyChange("s4", "foo").change_type()); - ASSERT_EQ( + EXPECT_EQ( SyncChange::ACTION_DELETE, sync_.GetOnlyChange("s4", "bar").change_type()); @@ -606,15 +608,15 @@ TEST_F(ExtensionSettingsSyncTest, ExtensionAndAppSettingsSyncSeparately) { std::map<std::string, SettingSyncDataList> extension_sync_data = GetAllSyncData(syncable::EXTENSION_SETTINGS); - ASSERT_EQ(1u, extension_sync_data.size()); - ASSERT_EQ(1u, extension_sync_data["s1"].size()); - ASSERT_PRED_FORMAT2(ValuesEq, &value1, &extension_sync_data["s1"][0].value()); + EXPECT_EQ(1u, extension_sync_data.size()); + EXPECT_EQ(1u, extension_sync_data["s1"].size()); + EXPECT_PRED_FORMAT2(ValuesEq, &value1, &extension_sync_data["s1"][0].value()); std::map<std::string, SettingSyncDataList> app_sync_data = GetAllSyncData(syncable::APP_SETTINGS); - ASSERT_EQ(1u, app_sync_data.size()); - ASSERT_EQ(1u, app_sync_data["s2"].size()); - ASSERT_PRED_FORMAT2(ValuesEq, &value2, &app_sync_data["s2"][0].value()); + EXPECT_EQ(1u, app_sync_data.size()); + EXPECT_EQ(1u, app_sync_data["s2"].size()); + EXPECT_PRED_FORMAT2(ValuesEq, &value2, &app_sync_data["s2"][0].value()); // Stop each separately, there should be no changes either time. SyncDataList sync_data; @@ -625,7 +627,7 @@ TEST_F(ExtensionSettingsSyncTest, ExtensionAndAppSettingsSyncSeparately) { MergeDataAndStartSyncing(syncable::EXTENSION_SETTINGS, sync_data, &sync_); GetSyncableService(syncable::EXTENSION_SETTINGS)-> StopSyncing(syncable::EXTENSION_SETTINGS); - ASSERT_EQ(0u, sync_.changes().size()); + EXPECT_EQ(0u, sync_.changes().size()); sync_data.clear(); sync_data.push_back(settings_sync_util::CreateData( @@ -635,7 +637,7 @@ TEST_F(ExtensionSettingsSyncTest, ExtensionAndAppSettingsSyncSeparately) { MergeDataAndStartSyncing(syncable::APP_SETTINGS, sync_data, &sync_); GetSyncableService(syncable::APP_SETTINGS)-> StopSyncing(syncable::APP_SETTINGS); - ASSERT_EQ(0u, sync_.changes().size()); + EXPECT_EQ(0u, sync_.changes().size()); } TEST_F(ExtensionSettingsSyncTest, FailingStartSyncingDisablesSync) { diff --git a/chrome/browser/extensions/settings/settings_test_util.cc b/chrome/browser/extensions/settings/settings_test_util.cc index c834c87..73793f3 100644 --- a/chrome/browser/extensions/settings/settings_test_util.cc +++ b/chrome/browser/extensions/settings/settings_test_util.cc @@ -7,6 +7,7 @@ #include "base/file_path.h" #include "chrome/common/extensions/extension.h" #include "chrome/browser/extensions/settings/settings_frontend.h" +#include "chrome/browser/extensions/settings/settings_namespace.h" namespace extensions { @@ -22,6 +23,7 @@ SettingsStorage* GetStorage( SettingsStorage* storage = NULL; frontend->RunWithStorage( extension_id, + settings_namespace::SYNC, base::Bind(&AssignStorage, &storage)); MessageLoop::current()->RunAllPending(); return storage; @@ -100,20 +102,22 @@ ExtensionEventRouter* MockProfile::GetExtensionEventRouter() { // ScopedSettingsFactory +ScopedSettingsStorageFactory::ScopedSettingsStorageFactory() {} + ScopedSettingsStorageFactory::ScopedSettingsStorageFactory( - SettingsStorageFactory* delegate) : delegate_(delegate) { - DCHECK(delegate); -} + const scoped_refptr<SettingsStorageFactory>& delegate) + : delegate_(delegate) {} ScopedSettingsStorageFactory::~ScopedSettingsStorageFactory() {} -void ScopedSettingsStorageFactory::Reset(SettingsStorageFactory* delegate) { - DCHECK(delegate); - delegate_.reset(delegate); +void ScopedSettingsStorageFactory::Reset( + const scoped_refptr<SettingsStorageFactory>& delegate) { + delegate_ = delegate; } SettingsStorage* ScopedSettingsStorageFactory::Create( const FilePath& base_path, const std::string& extension_id) { + DCHECK(delegate_.get()); return delegate_->Create(base_path, extension_id); } diff --git a/chrome/browser/extensions/settings/settings_test_util.h b/chrome/browser/extensions/settings/settings_test_util.h index ce78a6e5..822c73f 100644 --- a/chrome/browser/extensions/settings/settings_test_util.h +++ b/chrome/browser/extensions/settings/settings_test_util.h @@ -8,6 +8,8 @@ #include <string> +#include "base/compiler_specific.h" +#include "base/memory/linked_ptr.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/extensions/extension_event_router.h" @@ -69,19 +71,23 @@ class MockProfile : public TestingProfile { // SettingsStorageFactory which acts as a wrapper for other factories. class ScopedSettingsStorageFactory : public SettingsStorageFactory { public: - explicit ScopedSettingsStorageFactory(SettingsStorageFactory* delegate); + ScopedSettingsStorageFactory(); - virtual ~ScopedSettingsStorageFactory(); + explicit ScopedSettingsStorageFactory( + const scoped_refptr<SettingsStorageFactory>& delegate); // Sets the delegate factory (equivalent to scoped_ptr::reset). - void Reset(SettingsStorageFactory* delegate); + void Reset(const scoped_refptr<SettingsStorageFactory>& delegate); // SettingsStorageFactory implementation. virtual SettingsStorage* Create( const FilePath& base_path, const std::string& extension_id) OVERRIDE; private: - scoped_ptr<SettingsStorageFactory> delegate_; + // SettingsStorageFactory is refcounted. + virtual ~ScopedSettingsStorageFactory(); + + scoped_refptr<SettingsStorageFactory> delegate_; }; } // namespace settings_test_util diff --git a/chrome/browser/extensions/settings/syncable_settings_storage.cc b/chrome/browser/extensions/settings/syncable_settings_storage.cc index 613904f..c094e3a 100644 --- a/chrome/browser/extensions/settings/syncable_settings_storage.cc +++ b/chrome/browser/extensions/settings/syncable_settings_storage.cc @@ -5,6 +5,7 @@ #include "chrome/browser/extensions/settings/syncable_settings_storage.h" #include "base/memory/scoped_ptr.h" +#include "chrome/browser/extensions/settings/settings_namespace.h" #include "chrome/browser/extensions/settings/settings_sync_util.h" #include "chrome/browser/sync/api/sync_data.h" #include "chrome/browser/sync/protocol/extension_setting_specifics.pb.h" @@ -321,6 +322,7 @@ SyncError SyncableSettingsStorage::ProcessSyncChanges( observers_->Notify( &SettingsObserver::OnSettingsChanged, extension_id_, + settings_namespace::SYNC, SettingChange::GetEventJson(changes)); // TODO(kalman): Something sensible with multiple errors. diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index d869f90..b4c09eb 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1215,7 +1215,8 @@ 'browser/extensions/settings/settings_frontend.h', 'browser/extensions/settings/settings_leveldb_storage.cc', 'browser/extensions/settings/settings_leveldb_storage.h', - 'browser/extensions/settings/settings_observer.cc', + 'browser/extensions/settings/settings_namespace.cc', + 'browser/extensions/settings/settings_namespace.h', 'browser/extensions/settings/settings_observer.h', 'browser/extensions/settings/settings_storage.cc', 'browser/extensions/settings/settings_storage.h', diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index 4ded7d2..60b9d1c 100644 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -1033,103 +1033,107 @@ "optional": true } } - } - ], - "functions": [ + }, { - "name": "get", - "unprivileged": true, - "type": "function", - "description": "Gets one or more values from settings.", - "parameters": [ + "id": "StorageNamespace", + "type": "object", + "functions": [ { - "name": "keys", - "choices": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } }, + "name": "get", + "unprivileged": true, + "type": "function", + "description": "Gets one or more values from settings.", + "parameters": [ { - "type": "object", - "description": "Settings object to return in the callback, where the values are replaced with those from settings if they exist.", - "properties": {}, - "additionalProperties": { "type": "any" } + "name": "keys", + "choices": [ + { "type": "string" }, + { "type": "array", "items": { "type": "string" } }, + { + "type": "object", + "description": "Settings object to return in the callback, where the values are replaced with those from settings if they exist.", + "properties": {}, + "additionalProperties": { "type": "any" } + } + ], + "description": "A single key to get, list of keys to get, or a dictionary specifying default values (see description of the object). An empty list or object will return an empty settings object. Pass in null or undefined to get the entire contents of settings; this should only be used for debugging.", + "optional": true + }, + { + "name": "callback", + "type": "function", + "description": "Callback with settings values, or on failure (in which case lastError will be set).", + "parameters": [ + { + "name": "settings", + "type": "object", + "properties": {}, + "additionalProperties": { "type": "any" }, + "description": "Object with given keys set to settings values." + } + ] } - ], - "description": "A single key to get, list of keys to get, or a dictionary specifying default values (see description of the object). An empty list or object will return an empty settings object. Pass in null or undefined to get the entire contents of settings; this should only be used for debugging.", - "optional": true + ] }, { - "name": "callback", + "name": "set", + "unprivileged": true, "type": "function", - "description": "Callback with settings values, or on failure (in which case lastError will be set).", + "description": "Sets multiple settings values.", "parameters": [ { "name": "settings", "type": "object", "properties": {}, "additionalProperties": { "type": "any" }, - "description": "Object with given keys set to settings values." + "description": "Object to augment settings with. Values that cannot be serialized (functions, etc) will be ignored." + }, + { + "name": "callback", + "type": "function", + "description": "Callback on success, or on failure (in which case lastError will be set).", + "parameters": [], + "optional": true } ] - } - ] - }, - { - "name": "set", - "unprivileged": true, - "type": "function", - "description": "Sets multiple settings values.", - "parameters": [ - { - "name": "settings", - "type": "object", - "properties": {}, - "additionalProperties": { "type": "any" }, - "description": "Object to augment settings with. Values that cannot be serialized (functions, etc) will be ignored." }, { - "name": "callback", + "name": "remove", + "unprivileged": true, "type": "function", - "description": "Callback on success, or on failure (in which case lastError will be set).", - "parameters": [], - "optional": true - } - ] - }, - { - "name": "remove", - "unprivileged": true, - "type": "function", - "description": "Removes one or more values from settings.", - "parameters": [ - { - "name": "keys", - "choices": [ - {"type": "string"}, - {"type": "array", "items": {"type": "string"}, "minItems": 1} - ], - "description": "A single key or a list of keys to remove from settings." + "description": "Removes one or more values from settings.", + "parameters": [ + { + "name": "keys", + "choices": [ + {"type": "string"}, + {"type": "array", "items": {"type": "string"}, "minItems": 1} + ], + "description": "A single key or a list of keys to remove from settings." + }, + { + "name": "callback", + "type": "function", + "description": "Callback on success, or on failure (in which case lastError will be set).", + "parameters": [], + "optional": true + } + ] }, { - "name": "callback", - "type": "function", - "description": "Callback on success, or on failure (in which case lastError will be set).", - "parameters": [], - "optional": true - } - ] - }, - { - "name": "clear", - "unprivileged": true, - "type": "function", - "description": "Removes all values from settings.", - "parameters": [ - { - "name": "callback", + "name": "clear", + "unprivileged": true, "type": "function", - "description": "Callback on success, or on failure (in which case lastError will be set).", - "parameters": [], - "optional": true + "description": "Removes all values from settings.", + "parameters": [ + { + "name": "callback", + "type": "function", + "description": "Callback on success, or on failure (in which case lastError will be set).", + "parameters": [], + "optional": true + } + ] } ] } @@ -1145,10 +1149,27 @@ "name": "changes", "type": "array", "items": {"$ref": "SettingChange"} + }, + { + "name": "namespace", + "type": "string", + "description": "The namespace (e.g. sync or local) of the area the changes are for." } ] } - ] + ], + "properties": { + "sync": { + "$ref": "StorageNamespace", + "description": "Settings under the \"sync\" namespace are synced using Chrome Sync.", + "value": [ "sync" ] + }, + "local": { + "$ref": "StorageNamespace", + "description": "Settings under the \"local\" namespace are local to each machine.", + "value": [ "local" ] + } + } }, { "namespace": "windows", diff --git a/chrome/common/extensions/docs/experimental.settings.html b/chrome/common/extensions/docs/experimental.settings.html index 52714b2..81b7224 100644 --- a/chrome/common/extensions/docs/experimental.settings.html +++ b/chrome/common/extensions/docs/experimental.settings.html @@ -311,25 +311,21 @@ <li> <a href="#apiReference">API reference: chrome.experimental.settings</a> <ol> - <li style="display: none; "> + <li> <a href="#properties">Properties</a> <ol> <li> - <a href="#property-anchor">propertyName</a> + <a href="#property-sync">sync</a> + </li><li> + <a href="#property-local">local</a> </li> </ol> </li> - <li> - <a href="#global-methods">Methods</a> + <li style="display: none; "> + <a>Methods</a> <ol> <li> - <a href="#method-clear">clear</a> - </li><li> - <a href="#method-get">get</a> - </li><li> - <a href="#method-remove">remove</a> - </li><li> - <a href="#method-set">set</a> + <a href="#method-anchor">methodName</a> </li> </ol> </li> @@ -364,6 +360,32 @@ </ol> </li> </ol> + </li><li> + <a href="#type-StorageNamespace">StorageNamespace</a> + <ol> + <li> + <a href="#global-StorageNamespace-methods">Methods</a> + <ol> + <li> + <a href="#method-StorageNamespace-clear">clear</a> + </li><li> + <a href="#method-StorageNamespace-get">get</a> + </li><li> + <a href="#method-StorageNamespace-remove">remove</a> + </li><li> + <a href="#method-StorageNamespace-set">set</a> + </li> + </ol> + </li> + <li style="display: none; "> + <a>Events</a> + <ol> + <li> + <a href="#event-anchor">eventName</a> + </li> + </ol> + </li> + </ol> </li> </ol> </li> @@ -387,66 +409,111 @@ <h2>API reference: chrome.experimental.settings</h2> <!-- PROPERTIES --> - <div class="apiGroup" style="display: none; "> + <div class="apiGroup"> <a name="properties"></a> <h3 id="properties">Properties</h3> <div> - <a></a> - <h4>getLastError</h4> + <a name="property-sync"></a> + <h4>sync</h4> <div class="summary"> <!-- Note: intentionally longer 80 columns --> - <span>chrome.extension</span><span>lastError</span> + <span>chrome.experimental.settings.</span><span>sync</span> </div> <div> - </div> - </div> + <dt> + <var>sync</var> + <em> - </div> <!-- /apiGroup --> + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span> + <a href="experimental.settings.html#type-StorageNamespace">StorageNamespace</a> + </span> + <span style="display: none; "> + <span> + array of <span><span></span></span> + </span> + <span>paramType</span> + <span></span> + </span> + </span> + ) + </div> - <!-- METHODS --> - <div id="methodsTemplate" class="apiGroup"> - <a name="global-methods"></a> - <h3>Methods</h3> + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>Settings under the "sync" namespace are synced using Chrome Sync.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> - <!-- iterates over all functions --> - <div class="apiItem"> - <a name="method-clear"></a> <!-- method-anchor --> - <h4>clear</h4> + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> - <div class="summary"><span style="display: none; ">void</span> - <!-- Note: intentionally longer 80 columns --> - <span>chrome.experimental.settings.clear</span>(<span class="optional"><span style="display: none; ">, </span><span>function</span> - <var><span>callback</span></var></span>)</div> + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> - <div class="description"> - <p class="todo" style="display: none; ">Undocumented.</p> - <p>Removes all values from settings.</p> + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> - <!-- PARAMETERS --> - <h4>Parameters</h4> - <dl> - <div> - <div> + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <a name="property-local"></a> + <h4>local</h4> + <div class="summary"> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.experimental.settings.</span><span>local</span> + </div> + <div> <dt> - <var>callback</var> + <var>local</var> <em> <!-- TYPE --> <div style="display:inline"> ( - <span class="optional">optional</span> + <span class="optional" style="display: none; ">optional</span> <span class="enum" style="display: none; ">enumerated</span> <span id="typeTemplate"> - <span style="display: none; "> - <a> Type</a> - </span> <span> - <span style="display: none; "> + <a href="experimental.settings.html#type-StorageNamespace">StorageNamespace</a> + </span> + <span style="display: none; "> + <span> array of <span><span></span></span> </span> - <span>function</span> - <span style="display: none; "></span> + <span>paramType</span> + <span></span> </span> </span> ) @@ -457,7 +524,7 @@ <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Callback on success, or on failure (in which case lastError will be set).</dd> + <dd>Settings under the "local" namespace are local to each browser.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -494,13 +561,44 @@ </dd> </div> + </div> + + </div> <!-- /apiGroup --> + + <!-- METHODS --> + <div id="methodsTemplate" class="apiGroup" style="display: none; "> + <a></a> + <h3>Methods</h3> + + <!-- iterates over all functions --> + <div class="apiItem"> + <a></a> <!-- method-anchor --> + <h4>method name</h4> + + <div class="summary"><span>void</span> + <!-- Note: intentionally longer 80 columns --> + <span>chrome.module.methodName</span>(<span><span>, </span><span></span> + <var><span></span></var></span>)</div> + + <div class="description"> + <p class="todo">Undocumented.</p> + <p> + A description from the json schema def of the function goes here. + </p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> + </div> </div> </dl> <!-- RETURNS --> - <h4 style="display: none; ">Returns</h4> + <h4>Returns</h4> <dl> - <div style="display: none; "> + <div> <div> </div> </div> @@ -510,7 +608,7 @@ <div> <div> <h4>Callback function</h4> - <p style="display: none; "> + <p> The callback <em>parameter</em> should specify a function that looks like this: </p> @@ -520,9 +618,9 @@ </p> <!-- Note: intentionally longer 80 columns --> - <pre>function(<span></span>) <span class="subdued">{...}</span>;</pre> + <pre>function(<span>Type param1, Type param2</span>) <span class="subdued">{...}</span>;</pre> <dl> - <div style="display: none; "> + <div> <div> </div> </div> @@ -531,7 +629,7 @@ </div> <!-- MIN_VERSION --> - <p style="display: none; "> + <p> This function was added in version <b><span></span></b>. If you require this function, the manifest key <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> @@ -539,43 +637,63 @@ </p> </div> <!-- /description --> - </div><div class="apiItem"> - <a name="method-get"></a> <!-- method-anchor --> - <h4>get</h4> + </div> <!-- /apiItem --> - <div class="summary"><span style="display: none; ">void</span> - <!-- Note: intentionally longer 80 columns --> - <span>chrome.experimental.settings.get</span>(<span class="optional"><span style="display: none; ">, </span><span>string or array of string or object</span> - <var><span>keys</span></var></span><span class="null"><span>, </span><span>function</span> - <var><span>callback</span></var></span>)</div> + </div> <!-- /apiGroup --> + + <!-- EVENTS --> + <div id="eventsTemplate" class="apiGroup"> + <a name="global-events"></a> + <h3>Events</h3> + <!-- iterates over all events --> + <div class="apiItem"> + <a name="event-onChanged"></a> + <h4>onChanged</h4> + + <div class="summary"> + <!-- Note: intentionally longer 80 columns --> + <span class="subdued">chrome.experimental.settings.</span><span>onChanged</span><span class="subdued">.addListener</span>(function(<span>array of SettingChange changes, string namespace</span>) <span class="subdued">{...}</span><span></span>); + </div> <div class="description"> <p class="todo" style="display: none; ">Undocumented.</p> - <p>Gets one or more values from settings.</p> + <p>Fired when one or more settings change.</p> - <!-- PARAMETERS --> - <h4>Parameters</h4> - <dl> - <div> + <!-- LISTENER PARAMETERS --> + <div> + <h4>Listener parameters</h4> + <dl> <div> + <div> <dt> - <var>keys</var> + <var>changes</var> <em> <!-- TYPE --> <div style="display:inline"> ( - <span class="optional">optional</span> + <span class="optional" style="display: none; ">optional</span> <span class="enum" style="display: none; ">enumerated</span> <span id="typeTemplate"> <span style="display: none; "> <a> Type</a> </span> <span> - <span style="display: none; "> + <span> + array of <span><span> + <span> + <a href="experimental.settings.html#type-SettingChange">SettingChange</a> + </span> + <span style="display: none; "> + <span> array of <span><span></span></span> </span> - <span>string or array of string or object</span> + <span>paramType</span> + <span></span> + </span> + </span></span> + </span> + <span style="display: none; ">paramType</span> <span style="display: none; "></span> </span> </span> @@ -584,10 +702,12 @@ </em> </dt> - <dd class="todo" style="display: none; "> + <dd class="todo"> Undocumented. </dd> - <dd>A single key to get, list of keys to get, or a dictionary specifying default values (see description of the object). An empty list or object will return an empty settings object. Pass in null or undefined to get the entire contents of settings; this should only be used for debugging.</dd> + <dd style="display: none; "> + Description of this parameter from the json schema. + </dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -624,10 +744,10 @@ </dd> </div> - </div><div> - <div> + </div><div> + <div> <dt> - <var>callback</var> + <var>namespace</var> <em> <!-- TYPE --> @@ -643,7 +763,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>function</span> + <span>string</span> <span style="display: none; "></span> </span> </span> @@ -655,7 +775,7 @@ <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Callback with settings values, or on failure (in which case lastError will be set).</dd> + <dd>The namespace (e.g. sync or local) of the area the changes are for.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -692,11 +812,23 @@ </dd> </div> - </div> - </dl> + </div> + </dl> + </div> - <!-- RETURNS --> - <h4 style="display: none; ">Returns</h4> + <!-- EXTRA PARAMETERS --> + <div style="display: none; "> + <h4>Extra parameters to addListener</h4> + <dl> + <div> + <div> + </div> + </div> + </dl> + </div> + + <!-- LISTENER RETURN VALUE --> + <h4 style="display: none; ">Listener returns</h4> <dl> <div style="display: none; "> <div> @@ -704,26 +836,24 @@ </div> </dl> - <!-- CALLBACK --> - <div> - <div> - <h4>Callback function</h4> - <p> - The callback <em>parameter</em> should specify a function - that looks like this: - </p> - <p style="display: none; "> - If you specify the <em>callback</em> parameter, it should - specify a function that looks like this: - </p> + </div> <!-- /description --> + </div> <!-- /apiItem --> - <!-- Note: intentionally longer 80 columns --> - <pre>function(<span>object settings</span>) <span class="subdued">{...}</span>;</pre> - <dl> - <div> - <div> + </div> <!-- /apiGroup --> + + <!-- TYPES --> + <div class="apiGroup"> + <a name="types"></a> + <h3 id="types">Types</h3> + + <!-- iterates over all types --> + <div class="apiItem"> + <a name="type-SettingChange"></a> + <h4>SettingChange</h4> + + <div> <dt> - <var>settings</var> + <var style="display: none; ">paramName</var> <em> <!-- TYPE --> @@ -748,10 +878,12 @@ </em> </dt> - <dd class="todo" style="display: none; "> + <dd class="todo"> Undocumented. </dd> - <dd>Object with given keys set to settings values.</dd> + <dd style="display: none; "> + Description of this parameter from the json schema. + </dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -765,7 +897,52 @@ <!-- OBJECT PROPERTIES --> <dd> <dl> - <div style="display: none; "> + <div> + <div> + <dt> + <var>key</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The ID of the setting which changed.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> <div> </div> </div> @@ -788,47 +965,84 @@ </dd> </div> - </div> - </dl> - </div> + </div><div> + <div> + <dt> + <var>oldValue</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>any</span> + <span style="display: none; "></span> + </span> + </span> + ) </div> - <!-- MIN_VERSION --> - <p style="display: none; "> - This function was added in version <b><span></span></b>. - If you require this function, the manifest key - <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> - can ensure that your extension won't be run in an earlier browser version. - </p> - </div> <!-- /description --> + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>The old value of the setting, before it changed.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> - </div><div class="apiItem"> - <a name="method-remove"></a> <!-- method-anchor --> - <h4>remove</h4> + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> - <div class="summary"><span style="display: none; ">void</span> - <!-- Note: intentionally longer 80 columns --> - <span>chrome.experimental.settings.remove</span>(<span class="null"><span style="display: none; ">, </span><span>string or array of string</span> - <var><span>keys</span></var></span><span class="optional"><span>, </span><span>function</span> - <var><span>callback</span></var></span>)</div> + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> - <div class="description"> - <p class="todo" style="display: none; ">Undocumented.</p> - <p>Removes one or more values from settings.</p> + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> - <!-- PARAMETERS --> - <h4>Parameters</h4> - <dl> - <div> - <div> + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> <dt> - <var>keys</var> + <var>newValue</var> <em> <!-- TYPE --> <div style="display:inline"> ( - <span class="optional" style="display: none; ">optional</span> + <span class="optional">optional</span> <span class="enum" style="display: none; ">enumerated</span> <span id="typeTemplate"> <span style="display: none; "> @@ -838,7 +1052,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>string or array of string</span> + <span>any</span> <span style="display: none; "></span> </span> </span> @@ -850,7 +1064,7 @@ <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>A single key or a list of keys to remove from settings.</dd> + <dd>The new value of the setting.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -887,7 +1101,108 @@ </dd> </div> - </div><div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + + </div><div class="apiItem"> + <a name="type-StorageNamespace"></a> + <h4>StorageNamespace</h4> + + <div> + <dt> + <var style="display: none; ">paramName</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional" style="display: none; ">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>object</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo"> + Undocumented. + </dd> + <dd style="display: none; "> + Description of this parameter from the json schema. + </dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd> + <div class="apiGroup" style=""> + <a name="global-StorageNamespace-methods"></a> + <h3>Methods of StorageNamespace</h3> + + <!-- iterates over all functions --> + <div class="apiItem"> + <a name="method-StorageNamespace-clear"></a> <!-- method-anchor --> + <h4>clear</h4> + + <div class="summary"><span style="display: none; ">void</span> + <!-- Note: intentionally longer 80 columns --> + <span>storageNamespace.clear</span>(<span class="optional"><span style="display: none; ">, </span><span>function</span> + <var><span>callback</span></var></span>)</div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Removes all values from settings.</p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> <div> <dt> <var>callback</var> @@ -1001,18 +1316,18 @@ </div> <!-- /description --> </div><div class="apiItem"> - <a name="method-set"></a> <!-- method-anchor --> - <h4>set</h4> + <a name="method-StorageNamespace-get"></a> <!-- method-anchor --> + <h4>get</h4> <div class="summary"><span style="display: none; ">void</span> <!-- Note: intentionally longer 80 columns --> - <span>chrome.experimental.settings.set</span>(<span class="null"><span style="display: none; ">, </span><span>object</span> - <var><span>settings</span></var></span><span class="optional"><span>, </span><span>function</span> + <span>storageNamespace.get</span>(<span class="optional"><span style="display: none; ">, </span><span>string or array of string or object</span> + <var><span>keys</span></var></span><span class="null"><span>, </span><span>function</span> <var><span>callback</span></var></span>)</div> <div class="description"> <p class="todo" style="display: none; ">Undocumented.</p> - <p>Sets multiple settings values.</p> + <p>Gets one or more values from settings.</p> <!-- PARAMETERS --> <h4>Parameters</h4> @@ -1020,13 +1335,13 @@ <div> <div> <dt> - <var>settings</var> + <var>keys</var> <em> <!-- TYPE --> <div style="display:inline"> ( - <span class="optional" style="display: none; ">optional</span> + <span class="optional">optional</span> <span class="enum" style="display: none; ">enumerated</span> <span id="typeTemplate"> <span style="display: none; "> @@ -1036,7 +1351,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>object</span> + <span>string or array of string or object</span> <span style="display: none; "></span> </span> </span> @@ -1048,7 +1363,7 @@ <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Object to augment settings with. Values that cannot be serialized (functions, etc) will be ignored.</dd> + <dd>A single key to get, list of keys to get, or a dictionary specifying default values (see description of the object). An empty list or object will return an empty settings object. Pass in null or undefined to get the entire contents of settings; this should only be used for debugging.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1060,9 +1375,9 @@ </dd> <!-- OBJECT PROPERTIES --> - <dd> + <dd style="display: none; "> <dl> - <div style="display: none; "> + <div> <div> </div> </div> @@ -1094,7 +1409,7 @@ <!-- TYPE --> <div style="display:inline"> ( - <span class="optional">optional</span> + <span class="optional" style="display: none; ">optional</span> <span class="enum" style="display: none; ">enumerated</span> <span id="typeTemplate"> <span style="display: none; "> @@ -1116,7 +1431,7 @@ <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>Callback on success, or on failure (in which case lastError will be set).</dd> + <dd>Callback with settings values, or on failure (in which case lastError will be set).</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1169,65 +1484,22 @@ <div> <div> <h4>Callback function</h4> - <p style="display: none; "> + <p> The callback <em>parameter</em> should specify a function that looks like this: </p> - <p> + <p style="display: none; "> If you specify the <em>callback</em> parameter, it should specify a function that looks like this: </p> <!-- Note: intentionally longer 80 columns --> - <pre>function(<span></span>) <span class="subdued">{...}</span>;</pre> - <dl> - <div style="display: none; "> - <div> - </div> - </div> - </dl> - </div> - </div> - - <!-- MIN_VERSION --> - <p style="display: none; "> - This function was added in version <b><span></span></b>. - If you require this function, the manifest key - <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> - can ensure that your extension won't be run in an earlier browser version. - </p> - </div> <!-- /description --> - - </div> <!-- /apiItem --> - - </div> <!-- /apiGroup --> - - <!-- EVENTS --> - <div id="eventsTemplate" class="apiGroup"> - <a name="global-events"></a> - <h3>Events</h3> - <!-- iterates over all events --> - <div class="apiItem"> - <a name="event-onChanged"></a> - <h4>onChanged</h4> - - <div class="summary"> - <!-- Note: intentionally longer 80 columns --> - <span class="subdued">chrome.experimental.settings.</span><span>onChanged</span><span class="subdued">.addListener</span>(function(<span>array of SettingChange changes</span>) <span class="subdued">{...}</span><span></span>); - </div> - - <div class="description"> - <p class="todo" style="display: none; ">Undocumented.</p> - <p>Fired when one or more settings change.</p> - - <!-- LISTENER PARAMETERS --> - <div> - <h4>Listener parameters</h4> + <pre>function(<span>object settings</span>) <span class="subdued">{...}</span>;</pre> <dl> <div> <div> <dt> - <var>changes</var> + <var>settings</var> <em> <!-- TYPE --> @@ -1240,21 +1512,10 @@ <a> Type</a> </span> <span> - <span> - array of <span><span> - <span> - <a href="experimental.settings.html#type-SettingChange">SettingChange</a> - </span> - <span style="display: none; "> - <span> + <span style="display: none; "> array of <span><span></span></span> </span> - <span>paramType</span> - <span></span> - </span> - </span></span> - </span> - <span style="display: none; ">paramType</span> + <span>object</span> <span style="display: none; "></span> </span> </span> @@ -1263,12 +1524,10 @@ </em> </dt> - <dd class="todo"> + <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd style="display: none; "> - Description of this parameter from the json schema. - </dd> + <dd>Object with given keys set to settings values.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1280,9 +1539,9 @@ </dd> <!-- OBJECT PROPERTIES --> - <dd style="display: none; "> + <dd> <dl> - <div> + <div style="display: none; "> <div> </div> </div> @@ -1307,46 +1566,39 @@ </div> </div> </dl> - </div> - - <!-- EXTRA PARAMETERS --> - <div style="display: none; "> - <h4>Extra parameters to addListener</h4> - <dl> - <div> - <div> - </div> - </div> - </dl> - </div> - - <!-- LISTENER RETURN VALUE --> - <h4 style="display: none; ">Listener returns</h4> - <dl> - <div style="display: none; "> - <div> - </div> </div> - </dl> + </div> + <!-- MIN_VERSION --> + <p style="display: none; "> + This function was added in version <b><span></span></b>. + If you require this function, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </p> </div> <!-- /description --> - </div> <!-- /apiItem --> - </div> <!-- /apiGroup --> + </div><div class="apiItem"> + <a name="method-StorageNamespace-remove"></a> <!-- method-anchor --> + <h4>remove</h4> - <!-- TYPES --> - <div class="apiGroup"> - <a name="types"></a> - <h3 id="types">Types</h3> + <div class="summary"><span style="display: none; ">void</span> + <!-- Note: intentionally longer 80 columns --> + <span>storageNamespace.remove</span>(<span class="null"><span style="display: none; ">, </span><span>string or array of string</span> + <var><span>keys</span></var></span><span class="optional"><span>, </span><span>function</span> + <var><span>callback</span></var></span>)</div> - <!-- iterates over all types --> - <div class="apiItem"> - <a name="type-SettingChange"></a> - <h4>SettingChange</h4> + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Removes one or more values from settings.</p> - <div> + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> <dt> - <var style="display: none; ">paramName</var> + <var>keys</var> <em> <!-- TYPE --> @@ -1362,7 +1614,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>object</span> + <span>string or array of string</span> <span style="display: none; "></span> </span> </span> @@ -1371,12 +1623,10 @@ </em> </dt> - <dd class="todo"> + <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd style="display: none; "> - Description of this parameter from the json schema. - </dd> + <dd>A single key or a list of keys to remove from settings.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1388,18 +1638,41 @@ </dd> <!-- OBJECT PROPERTIES --> - <dd> + <dd style="display: none; "> <dl> <div> <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> + </div><div> + <div> <dt> - <var>key</var> + <var>callback</var> <em> <!-- TYPE --> <div style="display:inline"> ( - <span class="optional" style="display: none; ">optional</span> + <span class="optional">optional</span> <span class="enum" style="display: none; ">enumerated</span> <span id="typeTemplate"> <span style="display: none; "> @@ -1409,7 +1682,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>string</span> + <span>function</span> <span style="display: none; "></span> </span> </span> @@ -1421,7 +1694,7 @@ <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The ID of the setting which changed.</dd> + <dd>Callback on success, or on failure (in which case lastError will be set).</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1458,16 +1731,78 @@ </dd> </div> - </div><div> + </div> + </dl> + + <!-- RETURNS --> + <h4 style="display: none; ">Returns</h4> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + + <!-- CALLBACK --> <div> + <div> + <h4>Callback function</h4> + <p style="display: none; "> + The callback <em>parameter</em> should specify a function + that looks like this: + </p> + <p> + If you specify the <em>callback</em> parameter, it should + specify a function that looks like this: + </p> + + <!-- Note: intentionally longer 80 columns --> + <pre>function(<span></span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + </div> + </div> + + <!-- MIN_VERSION --> + <p style="display: none; "> + This function was added in version <b><span></span></b>. + If you require this function, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </p> + </div> <!-- /description --> + + </div><div class="apiItem"> + <a name="method-StorageNamespace-set"></a> <!-- method-anchor --> + <h4>set</h4> + + <div class="summary"><span style="display: none; ">void</span> + <!-- Note: intentionally longer 80 columns --> + <span>storageNamespace.set</span>(<span class="null"><span style="display: none; ">, </span><span>object</span> + <var><span>settings</span></var></span><span class="optional"><span>, </span><span>function</span> + <var><span>callback</span></var></span>)</div> + + <div class="description"> + <p class="todo" style="display: none; ">Undocumented.</p> + <p>Sets multiple settings values.</p> + + <!-- PARAMETERS --> + <h4>Parameters</h4> + <dl> + <div> + <div> <dt> - <var>oldValue</var> + <var>settings</var> <em> <!-- TYPE --> <div style="display:inline"> ( - <span class="optional">optional</span> + <span class="optional" style="display: none; ">optional</span> <span class="enum" style="display: none; ">enumerated</span> <span id="typeTemplate"> <span style="display: none; "> @@ -1477,7 +1812,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>any</span> + <span>object</span> <span style="display: none; "></span> </span> </span> @@ -1489,7 +1824,7 @@ <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The old value of the setting, before it changed.</dd> + <dd>Object to augment settings with. Values that cannot be serialized (functions, etc) will be ignored.</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1501,9 +1836,9 @@ </dd> <!-- OBJECT PROPERTIES --> - <dd style="display: none; "> + <dd> <dl> - <div> + <div style="display: none; "> <div> </div> </div> @@ -1526,10 +1861,10 @@ </dd> </div> - </div><div> - <div> + </div><div> + <div> <dt> - <var>newValue</var> + <var>callback</var> <em> <!-- TYPE --> @@ -1545,7 +1880,7 @@ <span style="display: none; "> array of <span><span></span></span> </span> - <span>any</span> + <span>function</span> <span style="display: none; "></span> </span> </span> @@ -1557,7 +1892,7 @@ <dd class="todo" style="display: none; "> Undocumented. </dd> - <dd>The new value of the setting.</dd> + <dd>Callback on success, or on failure (in which case lastError will be set).</dd> <dd style="display: none; "> This parameter was added in version <b><span></span></b>. @@ -1594,13 +1929,54 @@ </dd> </div> - </div> - </dl> - </dd> + </div> + </dl> - <!-- OBJECT METHODS --> - <dd style="display: none; "> - <div></div> + <!-- RETURNS --> + <h4 style="display: none; ">Returns</h4> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + + <!-- CALLBACK --> + <div> + <div> + <h4>Callback function</h4> + <p style="display: none; "> + The callback <em>parameter</em> should specify a function + that looks like this: + </p> + <p> + If you specify the <em>callback</em> parameter, it should + specify a function that looks like this: + </p> + + <!-- Note: intentionally longer 80 columns --> + <pre>function(<span></span>) <span class="subdued">{...}</span>;</pre> + <dl> + <div style="display: none; "> + <div> + </div> + </div> + </dl> + </div> + </div> + + <!-- MIN_VERSION --> + <p style="display: none; "> + This function was added in version <b><span></span></b>. + If you require this function, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </p> + </div> <!-- /description --> + + </div> <!-- /apiItem --> + + </div> </dd> <!-- OBJECT EVENT FIELDS --> diff --git a/chrome/common/extensions/docs/samples.json b/chrome/common/extensions/docs/samples.json index 3771549..649ba2e 100644 --- a/chrome/common/extensions/docs/samples.json +++ b/chrome/common/extensions/docs/samples.json @@ -68,11 +68,7 @@ "chrome.experimental.devtools.panels.setOpenResourceHandler": "experimental.devtools.panels.html#method-setOpenResourceHandler", "chrome.experimental.infobars.show": "experimental.infobars.html#method-show", "chrome.experimental.savePage.saveAsMHTML": "experimental.savePage.html#method-saveAsMHTML", - "chrome.experimental.settings.clear": "experimental.settings.html#method-clear", - "chrome.experimental.settings.get": "experimental.settings.html#method-get", "chrome.experimental.settings.onChanged": "experimental.settings.html#event-onChanged", - "chrome.experimental.settings.remove": "experimental.settings.html#method-remove", - "chrome.experimental.settings.set": "experimental.settings.html#method-set", "chrome.experimental.speechInput.isRecording": "experimental.speechInput.html#method-isRecording", "chrome.experimental.speechInput.onError": "experimental.speechInput.html#event-onError", "chrome.experimental.speechInput.onResult": "experimental.speechInput.html#event-onResult", diff --git a/chrome/renderer/resources/extensions/schema_generated_bindings.js b/chrome/renderer/resources/extensions/schema_generated_bindings.js index 3fe5c3e..3075d0d 100644 --- a/chrome/renderer/resources/extensions/schema_generated_bindings.js +++ b/chrome/renderer/resources/extensions/schema_generated_bindings.js @@ -384,6 +384,29 @@ var chrome = chrome || {}; customBindings['ContentSetting'] = ContentSetting; } + function setupStorageNamespace() { + function StorageNamespace(namespace, schema) { + // Binds an API function for a namespace to its browser-side call, e.g. + // experimental.settings.sync.get('foo') -> (binds to) -> + // experimental.settings.get('sync', 'foo'). + // + // TODO(kalman): Put as a method on CustomBindingsObject and re-use (or + // even generate) for other APIs that need to do this. + function bindApiFunction(functionName) { + this[functionName] = function() { + var schema = this.parameters[functionName]; + chromeHidden.validate(arguments, schema); + return sendRequest( + 'experimental.settings.' + functionName, + [namespace].concat(Array.prototype.slice.call(arguments)), + extendSchema(schema)); + }; + } + ['get', 'set', 'remove', 'clear'].forEach(bindApiFunction.bind(this)); + } + StorageNamespace.prototype = new CustomBindingsObject(); + customBindings['StorageNamespace'] = StorageNamespace; + } function setupInputEvents() { chrome.experimental.input.ime.onKeyEvent.dispatch = function(engineID, keyData) { @@ -580,10 +603,12 @@ var chrome = chrome || {}; // ChromeSetting objects from the API definition. setupChromeSetting(); - // Setup the ContentSetting class so we can use it to construct - // ContentSetting objects from the API definition. + // Ditto ContentSetting. setupContentSetting(); + // Ditto StorageNamespace. + setupStorageNamespace(); + // |apiFunctions| is a hash of name -> object that stores the // name & definition of the apiFunction. Custom handling of api functions // is implemented by adding a "handleRequest" function to the object. @@ -593,7 +618,7 @@ var chrome = chrome || {}; // TODO(rafaelw): Consider defining a json schema for an api definition // and validating either here, in a unit_test or both. // TODO(rafaelw): Handle synchronous functions. - // TOOD(rafaelw): Consider providing some convenient override points + // TODO(rafaelw): Consider providing some convenient override points // for api functions that wish to insert themselves into the call. var apiDefinitions = GetExtensionAPIDefinition(); var platform = getPlatform(); diff --git a/chrome/test/data/extensions/api_test/content_scripts/extension_api/fire_event.html b/chrome/test/data/extensions/api_test/content_scripts/extension_api/fire_event.html index dde1436..094d5c1 100644 --- a/chrome/test/data/extensions/api_test/content_scripts/extension_api/fire_event.html +++ b/chrome/test/data/extensions/api_test/content_scripts/extension_api/fire_event.html @@ -1,3 +1,3 @@ <script> -chrome.experimental.settings.set({foo:new Date().getTime()}); +chrome.experimental.settings.sync.set({foo:new Date().getTime()}); </script> diff --git a/chrome/test/data/extensions/api_test/settings/simple_test/background.html b/chrome/test/data/extensions/api_test/settings/simple_test/background.html index 8bb3c15..897ace0 100644 --- a/chrome/test/data/extensions/api_test/settings/simple_test/background.html +++ b/chrome/test/data/extensions/api_test/settings/simple_test/background.html @@ -1,55 +1,59 @@ <script> -// Calls chrome.test.succeed after the settings have been cleared for the next -// test. -function succeed() { - chrome.experimental.settings.clear(chrome.test.succeed); +function test(stage0) { + var apis = [ + chrome.experimental.settings.sync, + chrome.experimental.settings.local + ]; + apis.forEach(function(api) { + api.succeed = chrome.test.callbackPass(api.clear.bind(api)); + stage0.call(api); + }); } chrome.test.runTests([ function getWhenEmpty() { function stage0() { - chrome.experimental.settings.get('foo', stage1); + this.get('foo', stage1.bind(this)); } function stage1(settings) { chrome.test.assertEq({}, settings); - chrome.experimental.settings.get(['foo', 'bar'], stage2); + this.get(['foo', 'bar'], stage2.bind(this)); } function stage2(settings) { chrome.test.assertEq({}, settings); - chrome.experimental.settings.get(undefined, stage3); + this.get(undefined, stage3.bind(this)); } function stage3(settings) { chrome.test.assertEq({}, settings); - succeed(); + this.succeed(); } - stage0(); + test(stage0); }, function getWhenNonempty() { function stage0() { - chrome.experimental.settings.set({ + this.set({ 'foo' : 'bar', 'baz' : 'qux', 'hello': 'world' - }, stage1); + }, stage1.bind(this)); } function stage1() { - chrome.experimental.settings.get(['foo', 'baz'], stage2); + this.get(['foo', 'baz'], stage2.bind(this)); } function stage2(settings) { chrome.test.assertEq({ 'foo': 'bar', 'baz': 'qux' }, settings); - chrome.experimental.settings.get( - ['nothing', 'baz', 'hello', 'ignore'], stage3); + this.get(['nothing', 'baz', 'hello', 'ignore'], stage3.bind(this)); } function stage3(settings) { chrome.test.assertEq({ 'baz' : 'qux', 'hello': 'world' }, settings); - chrome.experimental.settings.get(null, stage4); + this.get(null, stage4.bind(this)); } function stage4(settings) { chrome.test.assertEq({ @@ -57,80 +61,77 @@ chrome.test.runTests([ 'baz' : 'qux', 'hello': 'world' }, settings); - succeed(); + this.succeed(); } - stage0(); + test(stage0); }, function removeWhenEmpty() { function stage0() { - chrome.experimental.settings.remove('foo', stage1); + this.remove('foo', stage1.bind(this)); } function stage1() { - chrome.experimental.settings.remove(['foo', 'bar'], stage2); + this.remove(['foo', 'bar'], this.succeed); } - function stage2() { - succeed(); - } - stage0(); + test(stage0); }, function removeWhenNonempty() { function stage0() { - chrome.experimental.settings.set({ + this.set({ 'foo' : 'bar', 'baz' : 'qux', 'hello': 'world' - }, stage1); + }, stage1.bind(this)); } function stage1() { - chrome.experimental.settings.remove('foo', stage2); + this.remove('foo', stage2.bind(this)); } function stage2() { - chrome.experimental.settings.get(null, stage3); + this.get(null, stage3.bind(this)); } function stage3(settings) { chrome.test.assertEq({ 'baz' : 'qux', 'hello': 'world' }, settings); - chrome.experimental.settings.remove(['baz', 'nothing'], stage4); + this.remove(['baz', 'nothing'], stage4.bind(this)); } function stage4() { - chrome.experimental.settings.get(null, stage5); + this.get(null, stage5.bind(this)); } function stage5(settings) { chrome.test.assertEq({ 'hello': 'world' }, settings); - chrome.experimental.settings.remove('hello', stage6); + this.remove('hello', stage6.bind(this)); } function stage6() { - chrome.experimental.settings.get(null, stage7); + this.get(null, stage7.bind(this)); } function stage7(settings) { chrome.test.assertEq({}, settings); - succeed(); + this.succeed(); } - stage0(); + test(stage0); }, function setWhenOverwriting() { function stage0() { - chrome.experimental.settings.set({ + this.set({ 'foo' : 'bar', 'baz' : 'qux', 'hello': 'world' - }, stage1); + }, stage1.bind(this)); } function stage1() { - chrome.experimental.settings.set({ + this.set({ 'foo' : 'otherBar', 'baz' : 'otherQux' - }, stage2); + }, stage2.bind(this)); } function stage2() { - chrome.experimental.settings.get(null, stage3); + this.get(null, stage3.bind(this)); } function stage3(settings) { chrome.test.assertEq({ @@ -138,14 +139,14 @@ chrome.test.runTests([ 'baz' : 'otherQux', 'hello': 'world' }, settings); - chrome.experimental.settings.set({ + this.set({ 'baz' : 'anotherQux', 'hello': 'otherWorld', 'some' : 'value' - }, stage4); + }, stage4.bind(this)); } function stage4() { - chrome.experimental.settings.get(null, stage5); + this.get(null, stage5.bind(this)); } function stage5(settings) { chrome.test.assertEq({ @@ -154,152 +155,157 @@ chrome.test.runTests([ 'hello': 'otherWorld', 'some' : 'value' }, settings); - succeed(); + this.succeed(); } - stage0(); + test(stage0); }, function clearWhenEmpty() { function stage0() { - chrome.experimental.settings.clear(stage1); + this.clear(stage1.bind(this)); } function stage1() { - chrome.experimental.settings.get(null, stage2); + this.get(null, stage2.bind(this)); } function stage2(settings) { chrome.test.assertEq({}, settings); - succeed(); + this.succeed(); } - stage0(); + test(stage0); }, function clearWhenNonempty() { function stage0() { - chrome.experimental.settings.set({ + this.set({ 'foo' : 'bar', 'baz' : 'qux', 'hello': 'world' - }, stage1); + }, stage1.bind(this)); } function stage1() { - chrome.experimental.settings.clear(stage2); + this.clear(stage2.bind(this)); } function stage2() { - chrome.experimental.settings.get(null, stage3); + this.get(null, stage3.bind(this)); } function stage3(settings) { chrome.test.assertEq({}, settings); - succeed(); + this.succeed(); } - stage0(); + test(stage0); }, function keysWithDots() { function stage0() { - chrome.experimental.settings.set({ + this.set({ 'foo.bar' : 'baz', 'one' : {'two': 'three'} - }, stage1); + }, stage1.bind(this)); } function stage1() { - chrome.experimental.settings.get(['foo.bar', 'one'], stage2); + this.get(['foo.bar', 'one'], stage2.bind(this)); } function stage2(settings) { chrome.test.assertEq({ 'foo.bar' : 'baz', 'one' : {'two': 'three'} }, settings); - chrome.experimental.settings.get('one.two', stage3); + this.get('one.two', stage3.bind(this)); } function stage3(settings) { chrome.test.assertEq({}, settings); - chrome.experimental.settings.remove(['foo.bar', 'one.two'], stage4); + this.remove(['foo.bar', 'one.two'], stage4.bind(this)); } function stage4() { - chrome.experimental.settings.get(null, stage5); + this.get(null, stage5.bind(this)); } function stage5(settings) { chrome.test.assertEq({ 'one' : {'two': 'three'} }, settings); - succeed(); + this.succeed(); } - stage0(); + test(stage0); }, function getWithDefaultValues() { function stage0() { - chrome.experimental.settings.get({ + this.get({ 'foo': 'defaultBar', 'baz': [1, 2, 3] - }, stage1); + }, stage1.bind(this)); } function stage1(settings) { chrome.test.assertEq({ 'foo': 'defaultBar', 'baz': [1, 2, 3] }, settings); - chrome.experimental.settings.get(null, stage2); + this.get(null, stage2.bind(this)); } function stage2(settings) { chrome.test.assertEq({}, settings); - chrome.experimental.settings.set({'foo': 'bar'}, stage3); + this.set({'foo': 'bar'}, stage3.bind(this)); } function stage3() { - chrome.experimental.settings.get({ + this.get({ 'foo': 'defaultBar', 'baz': [1, 2, 3] - }, stage4); + }, stage4.bind(this)); } function stage4(settings) { chrome.test.assertEq({ 'foo': 'bar', 'baz': [1, 2, 3] }, settings); - chrome.experimental.settings.set({'baz': {}}, stage5); + this.set({'baz': {}}, stage5.bind(this)); } function stage5() { - chrome.experimental.settings.get({ + this.get({ 'foo': 'defaultBar', 'baz': [1, 2, 3] - }, stage6); + }, stage6.bind(this)); } function stage6(settings) { chrome.test.assertEq({ 'foo': 'bar', 'baz': {} }, settings); - chrome.experimental.settings.remove('foo', stage7); + this.remove('foo', stage7.bind(this)); } function stage7() { - chrome.experimental.settings.get({ + this.get({ 'foo': 'defaultBar', 'baz': [1, 2, 3] - }, stage8); + }, stage8.bind(this)); } function stage8(settings) { chrome.test.assertEq({ 'foo': 'defaultBar', 'baz': {} }, settings); - succeed(); + this.succeed(); } - stage0(); + test(stage0); }, function throttling() { + // We can only really test one of the namespaces since they will all get + // throttled together. + var api = chrome.experimental.settings.sync; + // Should get throttled after 1000 calls. var maxRequests = 1001; + function next() { - chrome.experimental.settings.clear((--maxRequests > 0) ? next : done); + api.clear((--maxRequests > 0) ? next : done); } function done() { chrome.test.assertEq( "This request exceeds available quota.", chrome.extension.lastError.message); - succeed(); + chrome.test.succeed(); } - chrome.experimental.settings.clear(next); + api.clear(next); } ]); </script> diff --git a/chrome/test/data/extensions/api_test/settings/split_incognito/background.html b/chrome/test/data/extensions/api_test/settings/split_incognito/background.html index ad65ef5..9dae9f1 100644 --- a/chrome/test/data/extensions/api_test/settings/split_incognito/background.html +++ b/chrome/test/data/extensions/api_test/settings/split_incognito/background.html @@ -1,13 +1,16 @@ <script> -var settings = chrome.experimental.settings; +var api = chrome.experimental.settings; var assertEq = chrome.test.assertEq; var inIncognitoContext = chrome.extension.inIncognitoContext; -// All notifications received. -var notifications = []; -settings.onChanged.addListener(function(changes) { - changes.forEach(function(change) { - notifications.push(change); +['sync', 'local'].forEach(function(namespace) { + api[namespace].notifications = []; + api.onChanged.addListener(function(changes, event_namespace) { + if (event_namespace == namespace) { + changes.forEach(function(change) { + api[namespace].notifications.push(change); + }); + } }); }); @@ -18,49 +21,43 @@ settings.onChanged.addListener(function(changes) { // operation fully completes. var testActions = { noop: function(callback) { - settings.get("", callback); + this.get("", callback); }, assertEmpty: function(callback) { - settings.get(null, function(settings) { + this.get(null, function(settings) { chrome.test.assertEq({}, settings); callback(); }); }, assertFoo: function(callback) { - settings.get(null, function(settings) { + this.get(null, function(settings) { chrome.test.assertEq({foo: "bar"}, settings); callback(); }); }, setFoo: function(callback) { - settings.set({foo: "bar"}, callback); + this.set({foo: "bar"}, callback); }, removeFoo: function(callback) { - settings.remove("foo", callback); + this.remove("foo", callback); }, clear: function(callback) { - settings.clear(callback); + this.clear(callback); }, assertNoNotifications: function(callback) { - assertEq([], notifications); + assertEq([], this.notifications); callback(); }, clearNotifications: function(callback) { - notifications = []; + this.notifications.length = 0; callback(); }, assertAddFooNotification: function(callback) { - assertEq(1, notifications.length); - assertEq("foo", notifications[0].key); - assertEq(undefined, notifications[0].oldValue); - assertEq("bar", notifications[0].newValue); + assertEq([{ key: 'foo', newValue: 'bar' }], this.notifications); callback(); }, assertDeleteFooNotification: function(callback) { - assertEq(1, notifications.length); - assertEq("foo", notifications[0].key); - assertEq("bar", notifications[0].oldValue); - assertEq(undefined, notifications[0].newValue); + assertEq([{ key: 'foo', oldValue: 'bar' }], this.notifications); callback(); } }; @@ -73,7 +70,12 @@ function testEverything() { chrome.test.sendMessage(waiting, function(messageJson) { var message = JSON.parse(messageJson); var action = testActions[message.action]; - action(message.isFinalAction ? chrome.test.succeed : next); + if (!action) { + chrome.test.fail("Unknown action: " + message.action); + return; + } + action.bind(api[message.namespace])( + message.isFinalAction ? chrome.test.succeed : next); }); } next(); |