diff options
author | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-09 01:17:45 +0000 |
---|---|---|
committer | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-09 01:17:45 +0000 |
commit | 228b2ed855f581569f0b73f8b82dc999da394950 (patch) | |
tree | 11e9ec3e2a6cb1ef8f71bc66c5e34011f494667d | |
parent | 46a12790106b3cfb9e7fe8bcc26cb82eab2d0c15 (diff) | |
download | chromium_src-228b2ed855f581569f0b73f8b82dc999da394950.zip chromium_src-228b2ed855f581569f0b73f8b82dc999da394950.tar.gz chromium_src-228b2ed855f581569f0b73f8b82dc999da394950.tar.bz2 |
Make the settings API code internally synchronous (no change to the API itself)
and push the asynchronous component (running on the FILE thread) up the stack
to ExtensionSettingsApi.
BUG=
TEST=existing unit tests
Review URL: http://codereview.chromium.org/7838007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@100311 0039d316-1c4b-4281-b951-d872f2087c98
14 files changed, 684 insertions, 1123 deletions
diff --git a/chrome/browser/extensions/extension_settings.cc b/chrome/browser/extensions/extension_settings.cc index eb8f6e1..c3c3238 100644 --- a/chrome/browser/extensions/extension_settings.cc +++ b/chrome/browser/extensions/extension_settings.cc @@ -17,13 +17,12 @@ #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" ExtensionSettings::ExtensionSettings(const FilePath& base_path) - : base_path_(base_path) { -} + : base_path_(base_path) {} ExtensionSettings::~ExtensionSettings() { std::map<std::string, ExtensionSettingsStorage*>::iterator it; for (it = storage_objs_.begin(); it != storage_objs_.end(); ++it) { - it->second->DeleteSoon(); + BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, it->second); } } @@ -154,7 +153,7 @@ void ExtensionSettings::EndCreationOfStorage( if (existing == storage_objs_.end()) { storage_objs_[extension_id] = storage; } else { - storage->DeleteSoon(); + BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, storage); storage = existing->second; DCHECK(storage != NULL); } diff --git a/chrome/browser/extensions/extension_settings_api.cc b/chrome/browser/extensions/extension_settings_api.cc index 411d3b3..79bf586 100644 --- a/chrome/browser/extensions/extension_settings_api.cc +++ b/chrome/browser/extensions/extension_settings_api.cc @@ -7,101 +7,107 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_settings_api.h" #include "chrome/browser/profiles/profile.h" +#include "content/browser/browser_thread.h" namespace { const char* kUnsupportedArgumentType = "Unsupported argument type"; } // namespace -// StorageResultCallback - -SettingsFunction::StorageResultCallback::StorageResultCallback( - SettingsFunction* settings_function) - : settings_function_(settings_function) { -} - -SettingsFunction::StorageResultCallback::~StorageResultCallback() { -} - -void SettingsFunction::StorageResultCallback::OnSuccess( - DictionaryValue* settings) { - settings_function_->result_.reset(settings); - settings_function_->SendResponse(true); -} - -void SettingsFunction::StorageResultCallback::OnFailure( - const std::string& message) { - settings_function_->error_ = message; - settings_function_->SendResponse(false); -} - // SettingsFunction bool SettingsFunction::RunImpl() { profile()->GetExtensionService()->extension_settings()->GetStorage( extension_id(), - base::Bind(&SettingsFunction::RunWithStorage, this)); + base::Bind(&SettingsFunction::RunOnUIThreadWithStorage, this)); return true; } -void SettingsFunction::RunWithStorage(ExtensionSettingsStorage* storage) { - // Mimic how RunImpl() is handled in extensions code. - if (!RunWithStorageImpl(storage)) { - SendResponse(false); +void SettingsFunction::RunOnUIThreadWithStorage( + ExtensionSettingsStorage* storage) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + base::Bind(&SettingsFunction::RunOnFileThreadWithStorage, this, storage)); +} + +void SettingsFunction::RunOnFileThreadWithStorage( + ExtensionSettingsStorage* storage) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + bool success = RunOnFileThreadImpl(storage); + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&SettingsFunction::SendResponse, this, success)); +} + +bool SettingsFunction::UseResult( + const ExtensionSettingsStorage::Result& storage_result) { + if (storage_result.HasError()) { + error_ = storage_result.GetError(); + return false; } + DictionaryValue* settings = storage_result.GetSettings(); + result_.reset(settings == NULL ? NULL : settings->DeepCopy()); + return true; } // Concrete settings functions -bool GetSettingsFunction::RunWithStorageImpl( +// Adds all StringValues from a ListValue to a vector of strings. +static void AddAllStringValues( + const ListValue& from, std::vector<std::string>* to) { + DCHECK(to->empty()); + std::string as_string; + for (ListValue::const_iterator it = from.begin(); it != from.end(); ++it) { + if ((*it)->GetAsString(&as_string)) { + to->push_back(as_string); + } + } +} + +bool GetSettingsFunction::RunOnFileThreadImpl( ExtensionSettingsStorage* storage) { Value *input; EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input)); - std::string as_string; ListValue* as_list; if (input->IsType(Value::TYPE_NULL)) { - storage->Get(new StorageResultCallback(this)); + return UseResult(storage->Get()); } else if (input->GetAsString(&as_string)) { - storage->Get(as_string, new StorageResultCallback(this)); + return UseResult(storage->Get(as_string)); } else if (input->GetAsList(&as_list)) { - storage->Get(*as_list, new StorageResultCallback(this)); - } else { - error_ = kUnsupportedArgumentType; - return false; + std::vector<std::string> string_list; + AddAllStringValues(*as_list, &string_list); + return UseResult(storage->Get(string_list)); } - - return true; + return UseResult(ExtensionSettingsStorage::Result(kUnsupportedArgumentType)); } -bool SetSettingsFunction::RunWithStorageImpl( +bool SetSettingsFunction::RunOnFileThreadImpl( ExtensionSettingsStorage* storage) { DictionaryValue *input; EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &input)); - storage->Set(*input, new StorageResultCallback(this)); - return true; + return UseResult(storage->Set(*input)); } -bool RemoveSettingsFunction::RunWithStorageImpl( +bool RemoveSettingsFunction::RunOnFileThreadImpl( ExtensionSettingsStorage* storage) { Value *input; EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input)); - std::string as_string; ListValue* as_list; if (input->GetAsString(&as_string)) { - storage->Remove(as_string, new StorageResultCallback(this)); + return UseResult(storage->Remove(as_string)); } else if (input->GetAsList(&as_list)) { - storage->Remove(*as_list, new StorageResultCallback(this)); - } else { - error_ = kUnsupportedArgumentType; - return false; + std::vector<std::string> string_list; + AddAllStringValues(*as_list, &string_list); + return UseResult(storage->Remove(string_list)); } - - return true; + return UseResult(ExtensionSettingsStorage::Result(kUnsupportedArgumentType)); } -bool ClearSettingsFunction::RunWithStorageImpl( +bool ClearSettingsFunction::RunOnFileThreadImpl( ExtensionSettingsStorage* storage) { - storage->Clear(new StorageResultCallback(this)); - return true; + return UseResult(storage->Clear()); } diff --git a/chrome/browser/extensions/extension_settings_api.h b/chrome/browser/extensions/extension_settings_api.h index 6ddfd87..1c03539 100644 --- a/chrome/browser/extensions/extension_settings_api.h +++ b/chrome/browser/extensions/extension_settings_api.h @@ -14,33 +14,27 @@ // Superclass of all settings functions. class SettingsFunction : public AsyncExtensionFunction { public: - // Extension settings function implementations should do their work here, and - // either run a StorageResultCallback or fill the function result / call - // SendResponse themselves. - // The exception is that implementations can return false to immediately - // call SendResponse(false), for compliance with EXTENSION_FUNCTION_VALIDATE. - virtual bool RunWithStorageImpl(ExtensionSettingsStorage* storage) = 0; + // Extension settings function implementations should do their work here. + // This runs on the FILE thread. + // + // Implementations should fill in args themselves, though (like RunImpl) + // may return false to imply failure. + virtual bool RunOnFileThreadImpl(ExtensionSettingsStorage* storage) = 0; + protected: virtual bool RunImpl() OVERRIDE; - // Callback from all storage methods (Get/Set/Remove/Clear) which sets the - // appropriate fields of the extension function (result/error) and sends a - // response. - // Declared here to access to the protected members of ExtensionFunction. - class StorageResultCallback : public ExtensionSettingsStorage::Callback { - public: - explicit StorageResultCallback(SettingsFunction* settings_function); - virtual ~StorageResultCallback(); - virtual void OnSuccess(DictionaryValue* settings) OVERRIDE; - virtual void OnFailure(const std::string& message) OVERRIDE; - - private: - scoped_refptr<SettingsFunction> settings_function_; - }; + // Sets error_ or result_ depending on the value of a storage Result, and + // returns whether the Result implies success (i.e. !error). + bool UseResult(const ExtensionSettingsStorage::Result& storage_result); private: - // Callback method from GetStorage(); delegates to RunWithStorageImpl. - void RunWithStorage(ExtensionSettingsStorage* storage); + // Callback from GetStorage. + void RunOnUIThreadWithStorage(ExtensionSettingsStorage* storage); + + // Called from RunOnUIThreadWithStorage. Runs RunOnFileThreadImpl and sends + // a response (on the UI thread) with its return value. + void RunOnFileThreadWithStorage(ExtensionSettingsStorage* storage); }; class GetSettingsFunction : public SettingsFunction { @@ -48,7 +42,7 @@ class GetSettingsFunction : public SettingsFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.settings.get"); protected: - virtual bool RunWithStorageImpl(ExtensionSettingsStorage* storage) OVERRIDE; + virtual bool RunOnFileThreadImpl(ExtensionSettingsStorage* storage) OVERRIDE; }; class SetSettingsFunction : public SettingsFunction { @@ -56,7 +50,7 @@ class SetSettingsFunction : public SettingsFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.settings.set"); protected: - virtual bool RunWithStorageImpl(ExtensionSettingsStorage* storage) OVERRIDE; + virtual bool RunOnFileThreadImpl(ExtensionSettingsStorage* storage) OVERRIDE; }; class RemoveSettingsFunction : public SettingsFunction { @@ -64,7 +58,7 @@ class RemoveSettingsFunction : public SettingsFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.settings.remove"); protected: - virtual bool RunWithStorageImpl(ExtensionSettingsStorage* storage) OVERRIDE; + virtual bool RunOnFileThreadImpl(ExtensionSettingsStorage* storage) OVERRIDE; }; class ClearSettingsFunction : public SettingsFunction { @@ -72,7 +66,7 @@ class ClearSettingsFunction : public SettingsFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.settings.clear"); protected: - virtual bool RunWithStorageImpl(ExtensionSettingsStorage* storage) OVERRIDE; + virtual bool RunOnFileThreadImpl(ExtensionSettingsStorage* storage) OVERRIDE; }; #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_API_H_ diff --git a/chrome/browser/extensions/extension_settings_leveldb_storage.cc b/chrome/browser/extensions/extension_settings_leveldb_storage.cc index 5fcc99b..5ce5ad3 100644 --- a/chrome/browser/extensions/extension_settings_leveldb_storage.cc +++ b/chrome/browser/extensions/extension_settings_leveldb_storage.cc @@ -15,491 +15,231 @@ #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" namespace { - // Generic error message sent to extensions on failure. const char* kGenericOnFailureMessage = "Extension settings failed"; +} // namespace -// General closure type for computing a value on the FILE thread and calling -// back on the UI thread. The *OnFileThread methods should run on the file -// thread, all others should run on the UI thread. -// -// Subclasses should implement RunOnFileThread(), manipulating settings_ -// or error_, then call either SucceedOnFileThread() or FailOnFileThread(). -class ResultClosure { - public: - // Ownership of callback and settings are taken. - // Settings may be NULL (for Remove/Clear). - ResultClosure( - leveldb::DB* db, - DictionaryValue* settings, - ExtensionSettingsStorage::Callback* callback) - : db_(db), settings_(settings), callback_(callback) {} - - virtual ~ResultClosure() {} - - void Run() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - BrowserThread::PostTask( - BrowserThread::FILE, - FROM_HERE, - base::Bind(&ResultClosure::RunOnFileThread, base::Unretained(this))); - } - - protected: - virtual void RunOnFileThread() = 0; - - void SucceedOnFileThread() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind(&ResultClosure::Succeed, base::Unretained(this))); - } +/* static */ +ExtensionSettingsLeveldbStorage* ExtensionSettingsLeveldbStorage::Create( + const FilePath& base_path, + const std::string& extension_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + FilePath path = base_path.AppendASCII(extension_id); - void FailOnFileThread(std::string error) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - error_ = error; - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind(&ResultClosure::Fail, base::Unretained(this))); - } +#if defined(OS_POSIX) + std::string os_path(path.value()); +#elif defined(OS_WIN) + std::string os_path = base::SysWideToUTF8(path.value()); +#endif - // Helper for the Get() methods; reads a single key from the database using - // an existing leveldb options object. - // Returns whether the read was successful, in which case the given settings - // will have the value set. Otherwise, error will be set. - bool ReadFromDb( - leveldb::ReadOptions options, - const std::string& key, - DictionaryValue* settings, - std::string* error) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - std::string value_as_json; - leveldb::Status s = db_->Get(options, key, &value_as_json); - if (s.ok()) { - base::JSONReader json_reader; - Value* value = json_reader.JsonToValue(value_as_json, false, false); - if (value != NULL) { - settings->SetWithoutPathExpansion(key, value); - return true; - } else { - // TODO(kalman): clear the offending non-JSON value from the database. - LOG(ERROR) << "Invalid JSON: " << value_as_json; - *error = "Invalid JSON"; - return false; - } - } else if (s.IsNotFound()) { - // Despite there being no value, it was still a success. - return true; - } else { - LOG(ERROR) << "Error reading from database: " << s.ToString(); - *error = s.ToString(); - return false; - } + leveldb::Options options; + options.create_if_missing = true; + leveldb::DB* db; + leveldb::Status status = leveldb::DB::Open(options, os_path, &db); + if (!status.ok()) { + LOG(WARNING) << "Failed to create leveldb at " << path.value() << + ": " << status.ToString(); + return NULL; } + return new ExtensionSettingsLeveldbStorage(db); +} - // The leveldb database this closure interacts with. Owned by an - // ExtensionSettingsLeveldbStorage instance, but only accessed from the FILE - // thread part of the closure (RunOnFileThread), and only deleted on the FILE - // thread from DeleteSoon() method, guaranteed to run afterwards (see - // marked_for_deletion_). - leveldb::DB* db_; - - scoped_ptr<DictionaryValue> settings_; - std::string error_; - - private: - void Succeed() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - callback_->OnSuccess(settings_.release()); - delete this; - } +ExtensionSettingsLeveldbStorage::ExtensionSettingsLeveldbStorage( + leveldb::DB* db) : db_(db) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); +} - void Fail() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - callback_->OnFailure(error_); - delete this; - } +ExtensionSettingsLeveldbStorage::~ExtensionSettingsLeveldbStorage() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); +} - scoped_ptr<ExtensionSettingsStorage::Callback> callback_; - - DISALLOW_COPY_AND_ASSIGN(ResultClosure); -}; - -// Result closure for Get(key). -class Get1ResultClosure : public ResultClosure { - public: - Get1ResultClosure( - leveldb::DB* db, - ExtensionSettingsStorage::Callback* callback, - const std::string& key) - : ResultClosure(db, new DictionaryValue(), callback), key_(key) {} - - protected: - virtual void RunOnFileThread() OVERRIDE { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - if (ReadFromDb(leveldb::ReadOptions(), key_, settings_.get(), &error_)) { - SucceedOnFileThread(); - } else { - FailOnFileThread(kGenericOnFailureMessage); - } +ExtensionSettingsStorage::Result ExtensionSettingsLeveldbStorage::Get( + const std::string& key) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + scoped_ptr<DictionaryValue> settings(new DictionaryValue()); + if (!ReadFromDb(leveldb::ReadOptions(), key, settings.get())) { + return Result(kGenericOnFailureMessage); } + return Result(settings.release()); +} - private: - std::string key_; -}; - -// Result closure for Get(keys). -class Get2ResultClosure : public ResultClosure { - public: - // Ownership of keys is taken. - Get2ResultClosure( - leveldb::DB* db, - ExtensionSettingsStorage::Callback* callback, - ListValue* keys) - : ResultClosure(db, new DictionaryValue(), callback), keys_(keys) {} - - protected: - virtual void RunOnFileThread() OVERRIDE { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - leveldb::ReadOptions options; - // All interaction with the db is done on the same thread, so snapshotting - // isn't strictly necessary. This is just defensive. - options.snapshot = db_->GetSnapshot(); - bool success = true; - std::string key; - - for (ListValue::const_iterator it = keys_->begin(); - success && it != keys_->end(); ++it) { - if ((*it)->GetAsString(&key)) { - if (!ReadFromDb(options, key, settings_.get(), &error_)) { - success = false; - } - } - } - db_->ReleaseSnapshot(options.snapshot); +ExtensionSettingsStorage::Result ExtensionSettingsLeveldbStorage::Get( + const std::vector<std::string>& keys) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + leveldb::ReadOptions options; + // All interaction with the db is done on the same thread, so snapshotting + // isn't strictly necessary. This is just defensive. + scoped_ptr<DictionaryValue> settings(new DictionaryValue()); + bool success = true; - if (success) { - SucceedOnFileThread(); - } else { - FailOnFileThread(kGenericOnFailureMessage); - } + options.snapshot = db_->GetSnapshot(); + for (std::vector<std::string>::const_iterator it = keys.begin(); + success && it != keys.end(); ++it) { + success &= ReadFromDb(options, *it, settings.get()); } + db_->ReleaseSnapshot(options.snapshot); - private: - scoped_ptr<ListValue> keys_; -}; - -// Result closure for Get() . -class Get3ResultClosure : public ResultClosure { - public: - Get3ResultClosure( - leveldb::DB* db, - ExtensionSettingsStorage::Callback* callback) - : ResultClosure(db, new DictionaryValue(), callback) { + if (!success) { + return Result(kGenericOnFailureMessage); } - protected: - virtual void RunOnFileThread() OVERRIDE { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - base::JSONReader json_reader; - leveldb::ReadOptions options = leveldb::ReadOptions(); - // All interaction with the db is done on the same thread, so snapshotting - // isn't strictly necessary. This is just defensive. - options.snapshot = db_->GetSnapshot(); - scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options)); - - for (it->SeekToFirst(); it->Valid(); it->Next()) { - Value* value = - json_reader.JsonToValue(it->value().ToString(), false, false); - if (value != NULL) { - settings_->SetWithoutPathExpansion(it->key().ToString(), value); - } else { - // TODO(kalman): clear the offending non-JSON value from the database. - LOG(ERROR) << "Invalid JSON: " << it->value().ToString(); - } - } - db_->ReleaseSnapshot(options.snapshot); + return Result(settings.release()); +} - if (!it->status().ok()) { - LOG(ERROR) << "DB iteration failed: " << it->status().ToString(); - FailOnFileThread(kGenericOnFailureMessage); - } else { - SucceedOnFileThread(); - } - } -}; - -// Result closure for Set(key, value). -class Set1ResultClosure : public ResultClosure { - public: - // Ownership of the value is taken. - Set1ResultClosure( - leveldb::DB* db, - ExtensionSettingsStorage::Callback* callback, - const std::string& key, - Value* value) - : ResultClosure(db, new DictionaryValue(), callback), - key_(key), - value_(value) {} - - protected: - virtual void RunOnFileThread() OVERRIDE { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - std::string value_as_json; - base::JSONWriter::Write(value_.get(), false, &value_as_json); - leveldb::Status status = - db_->Put(leveldb::WriteOptions(), key_, value_as_json); - if (status.ok()) { - settings_->SetWithoutPathExpansion(key_, value_.release()); - SucceedOnFileThread(); +ExtensionSettingsStorage::Result ExtensionSettingsLeveldbStorage::Get() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + base::JSONReader json_reader; + leveldb::ReadOptions options = leveldb::ReadOptions(); + // All interaction with the db is done on the same thread, so snapshotting + // isn't strictly necessary. This is just defensive. + scoped_ptr<DictionaryValue> settings(new DictionaryValue()); + + options.snapshot = db_->GetSnapshot(); + scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options)); + for (it->SeekToFirst(); it->Valid(); it->Next()) { + Value* value = + json_reader.JsonToValue(it->value().ToString(), false, false); + if (value != NULL) { + settings->SetWithoutPathExpansion(it->key().ToString(), value); } else { - LOG(WARNING) << "DB write failed: " << status.ToString(); - FailOnFileThread(kGenericOnFailureMessage); + // TODO(kalman): clear the offending non-JSON value from the database. + LOG(ERROR) << "Invalid JSON: " << it->value().ToString(); } } + db_->ReleaseSnapshot(options.snapshot); - private: - std::string key_; - scoped_ptr<Value> value_; -}; - -// Result callback for Set(values). -class Set2ResultClosure : public ResultClosure { - public: - // Ownership of values is taken. - Set2ResultClosure( - leveldb::DB* db, - ExtensionSettingsStorage::Callback* callback, - DictionaryValue* values) - : ResultClosure(db, new DictionaryValue(), callback), values_(values) { + if (!it->status().ok()) { + LOG(ERROR) << "DB iteration failed: " << it->status().ToString(); + return Result(kGenericOnFailureMessage); } - protected: - virtual void RunOnFileThread() OVERRIDE { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - // Gather keys since a dictionary can't be modified during iteration. - std::vector<std::string> keys; - for (DictionaryValue::key_iterator it = values_->begin_keys(); - it != values_->end_keys(); ++it) { - keys.push_back(*it); - } - // Write values while transferring ownership from values_ to settings_. - std::string value_as_json; - leveldb::WriteBatch batch; - for (unsigned i = 0; i < keys.size(); ++i) { - Value* value = NULL; - values_->RemoveWithoutPathExpansion(keys[i], &value); - base::JSONWriter::Write(value, false, &value_as_json); - batch.Put(keys[i], value_as_json); - settings_->SetWithoutPathExpansion(keys[i], value); - } - DCHECK(values_->empty()); - - leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); - if (status.ok()) { - SucceedOnFileThread(); - } else { - LOG(WARNING) << "DB batch write failed: " << status.ToString(); - FailOnFileThread(kGenericOnFailureMessage); - } - } + return Result(settings.release()); +} - private: - scoped_ptr<DictionaryValue> values_; // will be empty on destruction -}; - -// Result closure for Remove(key). -class Remove1ResultClosure : public ResultClosure { - public: - Remove1ResultClosure( - leveldb::DB* db, - ExtensionSettingsStorage::Callback* callback, - const std::string& key) - : ResultClosure(db, NULL, callback), key_(key) { - } +ExtensionSettingsStorage::Result ExtensionSettingsLeveldbStorage::Set( + const std::string& key, const Value& value) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + std::string value_as_json; + base::JSONWriter::Write(&value, false, &value_as_json); - protected: - virtual void RunOnFileThread() OVERRIDE { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - leveldb::Status status = db_->Delete(leveldb::WriteOptions(), key_); - if (status.ok() || status.IsNotFound()) { - SucceedOnFileThread(); - } else { - LOG(WARNING) << "DB delete failed: " << status.ToString(); - FailOnFileThread(kGenericOnFailureMessage); - } + leveldb::Status status = + db_->Put(leveldb::WriteOptions(), key, value_as_json); + if (!status.ok()) { + LOG(WARNING) << "DB write failed: " << status.ToString(); + return Result(kGenericOnFailureMessage); } - private: - std::string key_; -}; - -// Result closure for Remove(keys). -class Remove2ResultClosure : public ResultClosure { - public: - // Ownership of keys is taken. - Remove2ResultClosure( - leveldb::DB* db, - ExtensionSettingsStorage::Callback* callback, - ListValue* keys) - : ResultClosure(db, NULL, callback), keys_(keys) { - } + DictionaryValue* settings = new DictionaryValue(); + settings->SetWithoutPathExpansion(key, value.DeepCopy()); + return Result(settings); +} - protected: - virtual void RunOnFileThread() OVERRIDE { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - std::string key; - leveldb::WriteBatch batch; - for (ListValue::const_iterator it = keys_->begin(); - it != keys_->end(); ++it) { - if ((*it)->GetAsString(&key)) { - batch.Delete(key); - } - } +ExtensionSettingsStorage::Result ExtensionSettingsLeveldbStorage::Set( + const DictionaryValue& settings) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + std::string value_as_json; + leveldb::WriteBatch batch; - leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); - if (status.ok() || status.IsNotFound()) { - SucceedOnFileThread(); - } else { - LOG(WARNING) << "DB batch delete failed: " << status.ToString(); - FailOnFileThread(kGenericOnFailureMessage); - } + for (DictionaryValue::key_iterator it = settings.begin_keys(); + it != settings.end_keys(); ++it) { + Value* value; + settings.GetWithoutPathExpansion(*it, &value); + base::JSONWriter::Write(value, false, &value_as_json); + batch.Put(*it, value_as_json); } - private: - scoped_ptr<ListValue> keys_; -}; - -// Result closure for Clear(). -class ClearResultClosure : public ResultClosure { - public: - ClearResultClosure(leveldb::DB* db, - ExtensionSettingsStorage::Callback* callback) - : ResultClosure(db, NULL, callback) { + leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); + if (!status.ok()) { + LOG(WARNING) << "DB batch write failed: " << status.ToString(); + return Result(kGenericOnFailureMessage); } - protected: - virtual void RunOnFileThread() OVERRIDE { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - leveldb::ReadOptions options; - // All interaction with the db is done on the same thread, so snapshotting - // isn't strictly necessary. This is just defensive. - options.snapshot = db_->GetSnapshot(); - scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options)); - leveldb::WriteBatch batch; - - for (it->SeekToFirst(); it->Valid(); it->Next()) { - batch.Delete(it->key()); - } - db_->ReleaseSnapshot(options.snapshot); - - if (it->status().ok()) { - leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); - if (status.ok() || status.IsNotFound()) { - SucceedOnFileThread(); - } else { - LOG(WARNING) << "Clear failed: " << status.ToString(); - FailOnFileThread(kGenericOnFailureMessage); - } - } else { - LOG(WARNING) << "Clear iteration failed: " << it->status().ToString(); - FailOnFileThread(kGenericOnFailureMessage); - } + return Result(settings.DeepCopy()); +} + +ExtensionSettingsStorage::Result ExtensionSettingsLeveldbStorage::Remove( + const std::string& key) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + leveldb::Status status = db_->Delete(leveldb::WriteOptions(), key); + if (!status.ok() && !status.IsNotFound()) { + LOG(WARNING) << "DB delete failed: " << status.ToString(); + return Result(kGenericOnFailureMessage); } -}; -} // namespace + return Result(NULL); +} -/* static */ -ExtensionSettingsLeveldbStorage* ExtensionSettingsLeveldbStorage::Create( - const FilePath& base_path, - const std::string& extension_id) { +ExtensionSettingsStorage::Result ExtensionSettingsLeveldbStorage::Remove( + const std::vector<std::string>& keys) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - FilePath path = base_path.AppendASCII(extension_id); - -#if defined(OS_POSIX) - std::string os_path(path.value()); -#elif defined(OS_WIN) - std::string os_path = base::SysWideToUTF8(path.value()); -#endif + leveldb::WriteBatch batch; + for (std::vector<std::string>::const_iterator it = keys.begin(); + it != keys.end(); ++it) { + batch.Delete(*it); + } - leveldb::Options options; - options.create_if_missing = true; - leveldb::DB* db; - leveldb::Status status = leveldb::DB::Open(options, os_path, &db); - if (!status.ok()) { - LOG(WARNING) << "Failed to create leveldb at " << path.value() << - ": " << status.ToString(); - return NULL; + leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); + if (!status.ok() && !status.IsNotFound()) { + LOG(WARNING) << "DB batch delete failed: " << status.ToString(); + return Result(kGenericOnFailureMessage); } - return new ExtensionSettingsLeveldbStorage(db); -} -ExtensionSettingsLeveldbStorage::ExtensionSettingsLeveldbStorage( - leveldb::DB* db) - : db_(db), marked_for_deletion_(false) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + return Result(NULL); } -ExtensionSettingsLeveldbStorage::~ExtensionSettingsLeveldbStorage() { +ExtensionSettingsStorage::Result ExtensionSettingsLeveldbStorage::Clear() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); -} + leveldb::ReadOptions options; + // All interaction with the db is done on the same thread, so snapshotting + // isn't strictly necessary. This is just defensive. + leveldb::WriteBatch batch; -void ExtensionSettingsLeveldbStorage::DeleteSoon() { - CHECK(!marked_for_deletion_); - marked_for_deletion_ = true; - BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, this); -} + options.snapshot = db_->GetSnapshot(); + scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options)); + for (it->SeekToFirst(); it->Valid(); it->Next()) { + batch.Delete(it->key()); + } + db_->ReleaseSnapshot(options.snapshot); -void ExtensionSettingsLeveldbStorage::Get( - const std::string& key, ExtensionSettingsStorage::Callback* callback) { - CHECK(!marked_for_deletion_); - (new Get1ResultClosure(db_.get(), callback, key))->Run(); -} + if (!it->status().ok()) { + LOG(WARNING) << "Clear iteration failed: " << it->status().ToString(); + return Result(kGenericOnFailureMessage); + } -void ExtensionSettingsLeveldbStorage::Get( - const ListValue& keys, ExtensionSettingsStorage::Callback* callback) { - CHECK(!marked_for_deletion_); - (new Get2ResultClosure(db_.get(), callback, keys.DeepCopy()))->Run(); -} + leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); + if (!status.ok() && !status.IsNotFound()) { + LOG(WARNING) << "Clear failed: " << status.ToString(); + return Result(kGenericOnFailureMessage); + } -void ExtensionSettingsLeveldbStorage::Get( - ExtensionSettingsStorage::Callback* callback) { - CHECK(!marked_for_deletion_); - (new Get3ResultClosure(db_.get(), callback))->Run(); + return Result(NULL); } -void ExtensionSettingsLeveldbStorage::Set( +bool ExtensionSettingsLeveldbStorage::ReadFromDb( + leveldb::ReadOptions options, const std::string& key, - const Value& value, - ExtensionSettingsStorage::Callback* callback) { - CHECK(!marked_for_deletion_); - (new Set1ResultClosure(db_.get(), callback, key, value.DeepCopy()))->Run(); -} + DictionaryValue* settings) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + std::string value_as_json; + leveldb::Status s = db_->Get(options, key, &value_as_json); -void ExtensionSettingsLeveldbStorage::Set( - const DictionaryValue& values, - ExtensionSettingsStorage::Callback* callback) { - CHECK(!marked_for_deletion_); - (new Set2ResultClosure(db_.get(), callback, values.DeepCopy()))->Run(); -} + if (s.IsNotFound()) { + // Despite there being no value, it was still a success. + return true; + } -void ExtensionSettingsLeveldbStorage::Remove( - const std::string& key, ExtensionSettingsStorage::Callback *callback) { - CHECK(!marked_for_deletion_); - (new Remove1ResultClosure(db_.get(), callback, key))->Run(); -} + if (!s.ok()) { + LOG(ERROR) << "Error reading from database: " << s.ToString(); + return false; + } -void ExtensionSettingsLeveldbStorage::Remove( - const ListValue& keys, ExtensionSettingsStorage::Callback *callback) { - CHECK(!marked_for_deletion_); - (new Remove2ResultClosure(db_.get(), callback, keys.DeepCopy()))->Run(); -} + Value* value = base::JSONReader().JsonToValue(value_as_json, false, false); + if (value == NULL) { + // TODO(kalman): clear the offending non-JSON value from the database. + LOG(ERROR) << "Invalid JSON in database: " << value_as_json; + return false; + } -void ExtensionSettingsLeveldbStorage::Clear( - ExtensionSettingsStorage::Callback* callback) { - CHECK(!marked_for_deletion_); - (new ClearResultClosure(db_.get(), callback))->Run(); + settings->SetWithoutPathExpansion(key, value); + return true; } diff --git a/chrome/browser/extensions/extension_settings_leveldb_storage.h b/chrome/browser/extensions/extension_settings_leveldb_storage.h index 79bbed3..5090f9a 100644 --- a/chrome/browser/extensions/extension_settings_leveldb_storage.h +++ b/chrome/browser/extensions/extension_settings_leveldb_storage.h @@ -17,44 +17,46 @@ #include "third_party/leveldatabase/src/include/leveldb/db.h" // Extension settings storage object, backed by a leveldb database. +// // No caching is done; that should be handled by wrapping with an // ExtensionSettingsStorageCache. +// All methods must be run on the FILE thread. class ExtensionSettingsLeveldbStorage : public ExtensionSettingsStorage { public: // Tries to create a leveldb storage area for an extension at a base path. // Returns NULL if creation fails. - // Must be run on the FILE thread. static ExtensionSettingsLeveldbStorage* Create( const FilePath& base_path, const std::string& extension_id); - // Can be run on any thread. - virtual void DeleteSoon() OVERRIDE; + // Must be deleted on the FILE thread. + virtual ~ExtensionSettingsLeveldbStorage(); - virtual void Get(const std::string& key, Callback* callback) OVERRIDE; - virtual void Get(const ListValue& keys, Callback* callback) OVERRIDE; - virtual void Get(Callback* callback) OVERRIDE; - virtual void Set( - const std::string& key, const Value& value, Callback* callback) OVERRIDE; - virtual void Set(const DictionaryValue& values, Callback* callback) OVERRIDE; - virtual void Remove(const std::string& key, Callback* callback) OVERRIDE; - virtual void Remove(const ListValue& keys, Callback* callback) OVERRIDE; - virtual void Clear(Callback *callback) OVERRIDE; + // ExtensionSettingsStorage implementation. + virtual Result Get(const std::string& key) OVERRIDE; + virtual Result Get(const std::vector<std::string>& keys) OVERRIDE; + virtual Result Get() OVERRIDE; + virtual Result Set(const std::string& key, const Value& value) OVERRIDE; + virtual Result Set(const DictionaryValue& settings) OVERRIDE; + virtual Result Remove(const std::string& key) OVERRIDE; + virtual Result Remove(const std::vector<std::string>& keys) OVERRIDE; + virtual Result Clear() OVERRIDE; private: // Ownership of db is taken. explicit ExtensionSettingsLeveldbStorage(leveldb::DB* db); - // This must only be deleted on the FILE thread. - friend class DeleteTask<ExtensionSettingsLeveldbStorage>; - virtual ~ExtensionSettingsLeveldbStorage(); + // Reads a key from the database, settings the resulting key/value pair in + // a given settings object if successful. + // Returns whether the get was successful. + bool ReadFromDb( + leveldb::ReadOptions options, + const std::string& key, + // Ownership NOT taken. + DictionaryValue* settings); // leveldb backend. scoped_ptr<leveldb::DB> db_; - // Whether this has or is about to be deleted on the FILE thread. - // Used to prevent any use of this after DeleteSoon() is called. - bool marked_for_deletion_; - DISALLOW_COPY_AND_ASSIGN(ExtensionSettingsLeveldbStorage); }; diff --git a/chrome/browser/extensions/extension_settings_noop_storage.cc b/chrome/browser/extensions/extension_settings_noop_storage.cc index b950be2..3e1199b 100644 --- a/chrome/browser/extensions/extension_settings_noop_storage.cc +++ b/chrome/browser/extensions/extension_settings_noop_storage.cc @@ -4,69 +4,42 @@ #include "chrome/browser/extensions/extension_settings_noop_storage.h" -#include "base/bind.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop.h" -#include "base/task.h" - -static void CallOnSuccess( - ExtensionSettingsStorage::Callback* callback, DictionaryValue* settings) { - callback->OnSuccess(settings); - delete callback; -} - -static void Succeed( - ExtensionSettingsStorage::Callback* callback, DictionaryValue* settings) { - MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&CallOnSuccess, callback, settings)); -} - -void ExtensionSettingsNoopStorage::DeleteSoon() { - delete this; -} - -void ExtensionSettingsNoopStorage::Get( - const std::string& key, ExtensionSettingsStorage::Callback* callback) { - Succeed(callback, new DictionaryValue()); +ExtensionSettingsStorage::Result ExtensionSettingsNoopStorage::Get( + const std::string& key) { + return Result(new DictionaryValue()); } -void ExtensionSettingsNoopStorage::Get( - const ListValue& keys, ExtensionSettingsStorage::Callback* callback) { - Succeed(callback, new DictionaryValue()); +ExtensionSettingsStorage::Result ExtensionSettingsNoopStorage::Get( + const std::vector<std::string>& keys) { + return Result(new DictionaryValue()); } -void ExtensionSettingsNoopStorage::Get( - ExtensionSettingsStorage::Callback* callback) { - Succeed(callback, new DictionaryValue()); +ExtensionSettingsStorage::Result ExtensionSettingsNoopStorage::Get() { + return Result(new DictionaryValue()); } -void ExtensionSettingsNoopStorage::Set( - const std::string& key, - const Value& value, - ExtensionSettingsStorage::Callback* callback) { +ExtensionSettingsStorage::Result ExtensionSettingsNoopStorage::Set( + const std::string& key, const Value& value) { DictionaryValue* settings = new DictionaryValue(); settings->SetWithoutPathExpansion(key, value.DeepCopy()); - Succeed(callback, settings); + return Result(settings); } -void ExtensionSettingsNoopStorage::Set( - const DictionaryValue& values, - ExtensionSettingsStorage::Callback* callback) { - Succeed(callback, values.DeepCopy()); +ExtensionSettingsStorage::Result ExtensionSettingsNoopStorage::Set( + const DictionaryValue& settings) { + return Result(settings.DeepCopy()); } -void ExtensionSettingsNoopStorage::Remove( - const std::string& key, ExtensionSettingsStorage::Callback *callback) { - Succeed(callback, NULL); +ExtensionSettingsStorage::Result ExtensionSettingsNoopStorage::Remove( + const std::string& key) { + return Result(NULL); } -void ExtensionSettingsNoopStorage::Remove( - const ListValue& keys, ExtensionSettingsStorage::Callback *callback) { - Succeed(callback, NULL); +ExtensionSettingsStorage::Result ExtensionSettingsNoopStorage::Remove( + const std::vector<std::string>& keys) { + return Result(NULL); } -void ExtensionSettingsNoopStorage::Clear( - ExtensionSettingsStorage::Callback* callback) { - Succeed(callback, NULL); +ExtensionSettingsStorage::Result ExtensionSettingsNoopStorage::Clear() { + return Result(NULL); } diff --git a/chrome/browser/extensions/extension_settings_noop_storage.h b/chrome/browser/extensions/extension_settings_noop_storage.h index 2112991..0396273 100644 --- a/chrome/browser/extensions/extension_settings_noop_storage.h +++ b/chrome/browser/extensions/extension_settings_noop_storage.h @@ -16,16 +16,15 @@ class ExtensionSettingsNoopStorage : public ExtensionSettingsStorage { public: ExtensionSettingsNoopStorage() {} - virtual void DeleteSoon() OVERRIDE; - virtual void Get(const std::string& key, Callback* callback) OVERRIDE; - virtual void Get(const ListValue& keys, Callback* callback) OVERRIDE; - virtual void Get(Callback* callback) OVERRIDE; - virtual void Set( - const std::string& key, const Value& value, Callback* callback) OVERRIDE; - virtual void Set(const DictionaryValue& values, Callback* callback) OVERRIDE; - virtual void Remove(const std::string& key, Callback* callback) OVERRIDE; - virtual void Remove(const ListValue& keys, Callback* callback) OVERRIDE; - virtual void Clear(Callback *callback) OVERRIDE; + // ExtensionSettingsStorage implementation. + virtual Result Get(const std::string& key) OVERRIDE; + virtual Result Get(const std::vector<std::string>& keys) OVERRIDE; + virtual Result Get() OVERRIDE; + virtual Result Set(const std::string& key, const Value& value) OVERRIDE; + virtual Result Set(const DictionaryValue& values) OVERRIDE; + virtual Result Remove(const std::string& key) OVERRIDE; + virtual Result Remove(const std::vector<std::string>& keys) OVERRIDE; + virtual Result Clear() OVERRIDE; private: DISALLOW_COPY_AND_ASSIGN(ExtensionSettingsNoopStorage); diff --git a/chrome/browser/extensions/extension_settings_storage.cc b/chrome/browser/extensions/extension_settings_storage.cc new file mode 100644 index 0000000..1e318b5 --- /dev/null +++ b/chrome/browser/extensions/extension_settings_storage.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 "base/logging.h" +#include "chrome/browser/extensions/extension_settings_storage.h" + +// Implementation of ExtensionSettingsStorage::Result + +ExtensionSettingsStorage::Result::Result(DictionaryValue* settings) + : inner_(new Inner(settings, std::string())) {} + +ExtensionSettingsStorage::Result::Result(const std::string& error) + : inner_(new Inner(NULL, error)) { + DCHECK(!error.empty()); +} + +ExtensionSettingsStorage::Result::~Result() {} + +DictionaryValue* ExtensionSettingsStorage::Result::GetSettings() const { + DCHECK(!HasError()); + return inner_->settings_.get(); +} + +bool ExtensionSettingsStorage::Result::HasError() const { + return !inner_->error_.empty(); +} + +const std::string& ExtensionSettingsStorage::Result::GetError() const { + DCHECK(HasError()); + return inner_->error_; +} + +ExtensionSettingsStorage::Result::Inner::Inner( + DictionaryValue* settings, const std::string& error) + : settings_(settings), error_(error) {} + +ExtensionSettingsStorage::Result::Inner::~Inner() {} diff --git a/chrome/browser/extensions/extension_settings_storage.h b/chrome/browser/extensions/extension_settings_storage.h index b1542c4..5918ab2 100644 --- a/chrome/browser/extensions/extension_settings_storage.h +++ b/chrome/browser/extensions/extension_settings_storage.h @@ -6,30 +6,16 @@ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_STORAGE_H_ #pragma once +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" #include "base/values.h" // Interface for extension settings storage classes. // -// All asynchrous methods *must* run in a message loop, i.e. the callbacks may -// not be run from the calling method, but must be PostTask'ed (whether to -// one's own thread or to e.g. the FILE thread). +// All methods *must* be run on the FILE thread, inclusing construction and +// destructions. class ExtensionSettingsStorage { public: - // Asynchronous results of Set/Get/Remove. Exactly one of OnSuccess or - // OnFailure will eventually be called. - // Callback objects will be deleted after running. - class Callback { - public: - virtual ~Callback() {} - - // Indicates the storage operation was successful. Settings value will be - // non-NULL. Ownership is passed to the callback. - virtual void OnSuccess(DictionaryValue* settings) = 0; - - // Indicates the storage operation failed. Messages describes the failure. - virtual void OnFailure(const std::string& message) = 0; - }; - // The different types of extension settings storage. enum Type { NONE, @@ -37,47 +23,74 @@ class ExtensionSettingsStorage { LEVELDB }; + // The result of an operation. + // + // Supports lightweight copying. + class Result { + public: + // Ownership of settings taken. + explicit Result(DictionaryValue* settings); + explicit Result(const std::string& error); + ~Result(); + + // The dictionary result of the computation. NULL does not imply invalid; + // HasError() should be used to test this. + // Wwnership remains with with the Result. + DictionaryValue* GetSettings() const; + + // Whether there was an error in the computation. If so, the results of + // GetSettings and ReleaseSettings are not valid. + bool HasError() const; + + // Gets the error message, if any. + const std::string& GetError() const; + + private: + struct Inner : public base::RefCountedThreadSafe<Inner> { + // Empty error implies no error. + Inner(DictionaryValue* settings, const std::string& error); + ~Inner(); + + const scoped_ptr<DictionaryValue> settings_; + const std::string error_; + }; + + const scoped_refptr<Inner> inner_; + }; + virtual ~ExtensionSettingsStorage() {} - // Destroys this settings storage object. This is needed as a separate - // interface method as opposed to just using the destructor, since - // destruction may need to be done asynchronously (e.g. on the FILE thread). - virtual void DeleteSoon() = 0; - - // Gets a single value from storage. Callback with a dictionary mapping the - // key to its value, if any. - virtual void Get(const std::string& key, Callback* callback) = 0; - - // Gets multiple values from storage. Callback with a dictionary mapping - // each key to its value, if any. - virtual void Get(const ListValue& keys, Callback* callback) = 0; - - // Gets all values from storage. Callback with a dictionary mapping every - // key to its value. - virtual void Get(Callback* callback) = 0; - - // Sets a single key to a new value. Callback with a dictionary mapping the - // key to its new value; on success, this is guaranteed to be the given key - // to the given new value. - virtual void Set(const std::string& key, const Value& value, - Callback* callback) = 0; - - // Sets multiple keys to new values. Callback with a dictionary mapping each - // key to its new value; on success, this is guaranteed to be each given key - // to its given new value. - virtual void Set(const DictionaryValue& values, Callback* callback) = 0; - - // Removes a key from the map. Callback with a dictionary mapping the key - // to its new value; on success, this will be an empty map. - virtual void Remove(const std::string& key, Callback* callback) = 0; - - // Removes multiple keys from the map. Callback with a dictionary mapping - // each key to its new value; on success, this will be an empty map. - virtual void Remove(const ListValue& keys, Callback* callback) = 0; - - // Clears the storage. Callback with a dictionary mapping every key in - // storage to its value; on success, this will be an empty map. - virtual void Clear(Callback *callback) = 0; + // Gets a single value from storage. + // If successful, result maps the key to its value. + virtual Result Get(const std::string& key) = 0; + + // Gets multiple values from storage. + // If successful, result maps each key to its value. + virtual Result Get(const std::vector<std::string>& keys) = 0; + + // Gets all values from storage. + // If successful, result maps every key to its value. + virtual Result Get() = 0; + + // Sets a single key to a new value. + // If successful, result maps the key to the given value. + virtual Result Set(const std::string& key, const Value& value) = 0; + + // Sets multiple keys to new values. + // If successful, result is identical to the given dictionary. + virtual Result Set(const DictionaryValue& values) = 0; + + // Removes a key from the storage. + // If successful, result value is NULL. + virtual Result Remove(const std::string& key) = 0; + + // Removes multiple keys from the storage. + // If successful, result value is NULL. + virtual Result Remove(const std::vector<std::string>& keys) = 0; + + // Clears the storage. + // If successful, result value is NULL. + virtual Result Clear() = 0; }; #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_STORAGE_H_ diff --git a/chrome/browser/extensions/extension_settings_storage_cache.cc b/chrome/browser/extensions/extension_settings_storage_cache.cc index 66ef5c9..2b5cedab 100644 --- a/chrome/browser/extensions/extension_settings_storage_cache.cc +++ b/chrome/browser/extensions/extension_settings_storage_cache.cc @@ -4,255 +4,129 @@ #include "chrome/browser/extensions/extension_settings_storage_cache.h" -#include "base/bind.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop.h" -#include "base/task.h" -#include "base/json/json_writer.h" - -namespace { +ExtensionSettingsStorageCache::ExtensionSettingsStorageCache( + ExtensionSettingsStorage* delegate) : delegate_(delegate) {} -void RemoveAllWithoutPathExpansion( - DictionaryValue* from, const ListValue* keys_to_remove) { - std::string key; - for (ListValue::const_iterator it = keys_to_remove->begin(); - it != keys_to_remove->end(); ++it) { - if ((*it)->GetAsString(&key)) { - from->RemoveWithoutPathExpansion(key, NULL); - } - } -} +ExtensionSettingsStorageCache::~ExtensionSettingsStorageCache() {} -// Callback which delegates to a given callback but caches any result if -// successful. Takes an optional parameter of existing settings to merge with -// before returning to the callback (may be NULL) and an optional parameter of -// keys to remove from the cache post-success (may be NULL). -class CacheModifyingCallback : public ExtensionSettingsStorage::Callback { - public: - // Takes ownership of callback, existing, and keys_to_remove. - CacheModifyingCallback( - ExtensionSettingsStorage::Callback* delegate, - base::WeakPtr<DictionaryValue> cache, - DictionaryValue* existing, - ListValue* keys_to_remove) - : delegate_(delegate), - cache_(cache), - existing_(existing), - keys_to_remove_(keys_to_remove) { - } - - static CacheModifyingCallback* Create( - ExtensionSettingsStorage::Callback* delegate, - base::WeakPtr<DictionaryValue> cache_ptr_) { - return new CacheModifyingCallback( - delegate, - cache_ptr_, - NULL, - NULL); +ExtensionSettingsStorage::Result ExtensionSettingsStorageCache::Get( + const std::string& key) { + Value *value; + if (GetFromCache(key, &value)) { + DictionaryValue* settings = new DictionaryValue(); + settings->SetWithoutPathExpansion(key, value); + return Result(settings); } - static CacheModifyingCallback* Create( - ExtensionSettingsStorage::Callback* delegate, - base::WeakPtr<DictionaryValue> cache_ptr_, - DictionaryValue* existing) { - return new CacheModifyingCallback( - delegate, - cache_ptr_, - existing, - NULL); + Result result = delegate_->Get(key); + if (result.HasError()) { + return result; } - static CacheModifyingCallback* Create( - ExtensionSettingsStorage::Callback* delegate, - base::WeakPtr<DictionaryValue> cache_ptr_, - ListValue* remove) { - return new CacheModifyingCallback( - delegate, - cache_ptr_, - NULL, - remove); - } + cache_.MergeDictionary(result.GetSettings()); + return result; +} - virtual void OnSuccess(DictionaryValue* settings) OVERRIDE { - // Note checking that the weak reference to the cache is still valid. - // It might be NULL if the owning ExtensionSettingsStorageCache has been - // deleted. - // Also, settings may be NULL for Remove/Clear. - if (settings != NULL && cache_.get() != NULL) { - cache_->MergeDictionary(settings); - } - if (settings != NULL && existing_ != NULL) { - settings->MergeDictionary(existing_.get()); +ExtensionSettingsStorage::Result ExtensionSettingsStorageCache::Get( + const std::vector<std::string>& keys) { + scoped_ptr<DictionaryValue> from_cache(new DictionaryValue()); + std::vector<std::string> missing_keys; + + for (std::vector<std::string>::const_iterator it = keys.begin(); + it != keys.end(); ++it) { + Value *value; + if (GetFromCache(*it, &value)) { + from_cache->SetWithoutPathExpansion(*it, value); + } else { + missing_keys.push_back(*it); } - if (cache_.get() != NULL && keys_to_remove_ != NULL) { - RemoveAllWithoutPathExpansion(cache_, keys_to_remove_.get()); - } - delegate_->OnSuccess(settings); } - virtual void OnFailure(const std::string& message) OVERRIDE { - delegate_->OnFailure(message); + if (missing_keys.empty()) { + return Result(from_cache.release()); } - private: - scoped_ptr<ExtensionSettingsStorage::Callback> delegate_; - base::WeakPtr<DictionaryValue> cache_; - scoped_ptr<DictionaryValue> existing_; - scoped_ptr<ListValue> keys_to_remove_; -}; - -} // namespace + Result result = delegate_->Get(keys); + if (result.HasError()) { + return result; + } -ExtensionSettingsStorageCache::ExtensionSettingsStorageCache( - ExtensionSettingsStorage* delegate) - : delegate_(delegate), cache_ptr_factory_(&cache_) { + cache_.MergeDictionary(result.GetSettings()); + result.GetSettings()->MergeDictionary(from_cache.get()); + return result; } -ExtensionSettingsStorageCache::~ExtensionSettingsStorageCache() { -} +ExtensionSettingsStorage::Result ExtensionSettingsStorageCache::Get() { + Result result = delegate_->Get(); + if (result.HasError()) { + return result; + } -void ExtensionSettingsStorageCache::DeleteSoon() { - delegate_->DeleteSoon(); - delete this; + cache_.MergeDictionary(result.GetSettings()); + // Hack so that the cached settings are used when the delegate is a no-op + // storage, in which case Get() will always return empty settings. + if (result.GetSettings()->empty()) { + return Result(cache_.DeepCopy()); + } + + return result; } -void ExtensionSettingsStorageCache::Get(const std::string& key, - ExtensionSettingsStorageCache::Callback* callback) { - Value *value; - if (GetFromCache(key, &value)) { - DictionaryValue* settings = new DictionaryValue(); - settings->SetWithoutPathExpansion(key, value); - MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind( - &ExtensionSettingsStorageCache::Callback::OnSuccess, - base::Unretained(callback), - settings)); - } else { - delegate_->Get( - key, - CacheModifyingCallback::Create( - callback, - cache_ptr_factory_.GetWeakPtr())); +ExtensionSettingsStorage::Result ExtensionSettingsStorageCache::Set( + const std::string& key, const Value& value) { + Result result = delegate_->Set(key, value); + if (result.HasError()) { + return result; } -} -void ExtensionSettingsStorageCache::Get(const ListValue& keys, - ExtensionSettingsStorageCache::Callback* callback) { - std::string key; - DictionaryValue* settings = new DictionaryValue(); - ListValue missing_keys; + cache_.MergeDictionary(result.GetSettings()); + return result; +} - for (ListValue::const_iterator it = keys.begin(); it != keys.end(); ++it) { - if ((*it)->GetAsString(&key)) { - Value *value; - if (GetFromCache(key, &value)) { - settings->SetWithoutPathExpansion(key, value); - } else { - missing_keys.Append(Value::CreateStringValue(key)); - } - } +ExtensionSettingsStorage::Result ExtensionSettingsStorageCache::Set( + const DictionaryValue& settings) { + Result result = delegate_->Set(settings); + if (result.HasError()) { + return result; } - if (missing_keys.empty()) { - MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind( - &ExtensionSettingsStorageCache::Callback::OnSuccess, - base::Unretained(callback), - settings)); - } else { - delegate_->Get( - missing_keys, - CacheModifyingCallback::Create( - callback, - cache_ptr_factory_.GetWeakPtr(), - settings)); - } + cache_.MergeDictionary(result.GetSettings()); + return result; } -void ExtensionSettingsStorageCache::Get( - ExtensionSettingsStorageCache::Callback* callback) { - // Copy the cache when passing in, as a semi-hack so that caching a no-op - // storage object works (which always returns empty from Get()). - delegate_->Get( - CacheModifyingCallback::Create( - callback, - cache_ptr_factory_.GetWeakPtr(), - cache_.DeepCopy())); -} +ExtensionSettingsStorage::Result ExtensionSettingsStorageCache::Remove( + const std::string& key) { + Result result = delegate_->Remove(key); + if (result.HasError()) { + return result; + } -void ExtensionSettingsStorageCache::Set( - const std::string& key, - const Value& value, - ExtensionSettingsStorageCache::Callback* callback) { - // Invalidate the cached entry first, in case the set fails. cache_.RemoveWithoutPathExpansion(key, NULL); - delegate_->Set( - key, - value, - CacheModifyingCallback::Create( - callback, - cache_ptr_factory_.GetWeakPtr())); + return result; } -void ExtensionSettingsStorageCache::Set( - const DictionaryValue& values, - ExtensionSettingsStorageCache::Callback* callback) { - // Invalidate the cached entries first, in case the set fails. - for (DictionaryValue::key_iterator it = values.begin_keys(); - it != values.end_keys(); ++it) { - cache_.RemoveWithoutPathExpansion(*it, NULL); +ExtensionSettingsStorage::Result ExtensionSettingsStorageCache::Remove( + const std::vector<std::string>& keys) { + Result result = delegate_->Remove(keys); + if (result.HasError()) { + return result; } - delegate_->Set( - values, - CacheModifyingCallback::Create( - callback, - cache_ptr_factory_.GetWeakPtr())); -} -void ExtensionSettingsStorageCache::Remove( - const std::string& key, - ExtensionSettingsStorageCache::Callback *callback) { - // Invalidate the cached entry first, in case the remove fails. - // We will also need to do if after the callback, to avoid race conditions - // whether other API calls fill the cache on the UI thread. - // This would be a good time to use structured cloning... - cache_.RemoveWithoutPathExpansion(key, NULL); - ListValue* key_list = new ListValue(); - key_list->Append(Value::CreateStringValue(key)); - delegate_->Remove( - key, - CacheModifyingCallback::Create( - callback, - cache_ptr_factory_.GetWeakPtr(), - key_list)); + for (std::vector<std::string>::const_iterator it = keys.begin(); + it != keys.end(); ++it) { + cache_.RemoveWithoutPathExpansion(*it, NULL); + } + return result; } -void ExtensionSettingsStorageCache::Remove( - const ListValue& keys, - ExtensionSettingsStorageCache::Callback *callback) { - std::string key; - // Invalidate each cached entry first, in case the remove fails. - // We will also need to do if after the callback, to avoid race conditions - // whether other API calls fill the cache on the UI thread. - RemoveAllWithoutPathExpansion(&cache_, &keys); - delegate_->Remove( - keys, - CacheModifyingCallback::Create( - callback, - cache_ptr_factory_.GetWeakPtr(), - keys.DeepCopy())); -} +ExtensionSettingsStorage::Result ExtensionSettingsStorageCache::Clear() { + Result result = delegate_->Clear(); + if (result.HasError()) { + return result; + } -void ExtensionSettingsStorageCache::Clear( - ExtensionSettingsStorageCache::Callback* callback) { cache_.Clear(); - delegate_->Clear( - CacheModifyingCallback::Create( - callback, - cache_ptr_factory_.GetWeakPtr())); + return result; } bool ExtensionSettingsStorageCache::GetFromCache( @@ -261,6 +135,7 @@ bool ExtensionSettingsStorageCache::GetFromCache( if (!cache_.GetWithoutPathExpansion(key, &cached_value)) { return false; } + *value = cached_value->DeepCopy(); return true; } diff --git a/chrome/browser/extensions/extension_settings_storage_cache.h b/chrome/browser/extensions/extension_settings_storage_cache.h index 460f44c..3e4e5d1 100644 --- a/chrome/browser/extensions/extension_settings_storage_cache.h +++ b/chrome/browser/extensions/extension_settings_storage_cache.h @@ -7,6 +7,7 @@ #pragma once #include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/extensions/extension_settings_storage.h" @@ -15,38 +16,34 @@ // // Calls to Get() will return from the cache if an entry is present, or be // passed to the delegate and the result cached if not. -// // Calls to Set() / Clear() / Remove() are written through to the delegate, // then stored in the cache if successful. class ExtensionSettingsStorageCache : public ExtensionSettingsStorage { public: - // Creates a cache around a delegate. Ownership of the delegate is taken. + // Ownership of delegate taken. explicit ExtensionSettingsStorageCache(ExtensionSettingsStorage* delegate); + virtual ~ExtensionSettingsStorageCache(); - virtual void DeleteSoon() OVERRIDE; - - virtual void Get(const std::string& key, Callback* callback) OVERRIDE; - virtual void Get(const ListValue& keys, Callback* callback) OVERRIDE; - virtual void Get(Callback* callback) OVERRIDE; - virtual void Set( - const std::string& key, const Value& value, Callback* callback) OVERRIDE; - virtual void Set(const DictionaryValue& values, Callback* callback) OVERRIDE; - virtual void Remove(const std::string& key, Callback* callback) OVERRIDE; - virtual void Remove(const ListValue& keys, Callback* callback) OVERRIDE; - virtual void Clear(Callback *callback) OVERRIDE; + // ExtensionSettingsStorage implementation. + virtual Result Get(const std::string& key) OVERRIDE; + virtual Result Get(const std::vector<std::string>& keys) OVERRIDE; + virtual Result Get() OVERRIDE; + virtual Result Set(const std::string& key, const Value& value) OVERRIDE; + virtual Result Set(const DictionaryValue& settings) OVERRIDE; + virtual Result Remove(const std::string& key) OVERRIDE; + virtual Result Remove(const std::vector<std::string>& keys) OVERRIDE; + virtual Result Clear() OVERRIDE; private: - // Delete with DeleteSoon(). - virtual ~ExtensionSettingsStorageCache(); - // Returns whether the value was found in the cache. // Ownership of value is released to the caller and placed in value. bool GetFromCache(const std::string& key, Value** value); - // Deleted in DeleteSoon(). - ExtensionSettingsStorage* delegate_; + // Storage that the cache is wrapping. + scoped_ptr<ExtensionSettingsStorage> delegate_; + + // The in-memory cache of settings from the delegate. DictionaryValue cache_; - base::WeakPtrFactory<DictionaryValue> cache_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(ExtensionSettingsStorageCache); }; diff --git a/chrome/browser/extensions/extension_settings_storage_unittest.cc b/chrome/browser/extensions/extension_settings_storage_unittest.cc index ccbbf16..5b51f3f 100644 --- a/chrome/browser/extensions/extension_settings_storage_unittest.cc +++ b/chrome/browser/extensions/extension_settings_storage_unittest.cc @@ -13,116 +13,79 @@ #include "chrome/browser/extensions/extension_settings.h" #include "content/browser/browser_thread.h" -// Define macro to get the __LINE__ expansion. -#define NEW_CALLBACK(expected) \ - (new AssertEqualsCallback((expected), __LINE__)) - -namespace { +// Gets the pretty-printed JSON for a value. +static std::string GetJSON(const Value& value) { + std::string json; + base::JSONWriter::Write(&value, true, &json); + return json; +} -// Callback from storage methods which performs the test assertions. -class AssertEqualsCallback : public ExtensionSettingsStorage::Callback { - public: - AssertEqualsCallback(DictionaryValue* expected, int line) - : expected_(expected), line_(line), called_(false) { +// Returns whether the result of a storage operation is an expected value. +testing::AssertionResult SettingsEq( + const char* expected_expr, const char* actual_expr, + DictionaryValue* expected, ExtensionSettingsStorage::Result actual) { + if (actual.HasError()) { + return testing::AssertionFailure() << + "Expected: " << GetJSON(*expected) << + ", actual has error: " << actual.GetError(); } - - ~AssertEqualsCallback() { - // Need to DCHECK since ASSERT_* can't be used from destructors. - DCHECK(called_); + if (expected == actual.GetSettings()) { + return testing::AssertionSuccess(); } - - virtual void OnSuccess(DictionaryValue* actual) OVERRIDE { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - ASSERT_FALSE(called_) << "Callback has already been called"; - called_ = true; - if (expected_ == NULL) { - ASSERT_TRUE(actual == NULL) << "Values are different:\n" << - "Line: " << line_ << "\n" << - "Expected: NULL\n" << - "Got: " << GetJson(actual); - } else { - ASSERT_TRUE(expected_->Equals(actual)) << "Values are different:\n" << - "Line: " << line_ << "\n" << - "Expected: " << GetJson(expected_) << - "Got: " << GetJson(actual); - delete actual; - } + if (expected == NULL && actual.GetSettings() != NULL) { + return testing::AssertionFailure() << + "Expected NULL, actual: " << GetJSON(*actual.GetSettings()); } - - virtual void OnFailure(const std::string& message) OVERRIDE { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - ASSERT_FALSE(called_) << "Callback has already been called"; - called_ = true; - // No tests allow failure (yet). - ASSERT_TRUE(false) << "Callback failed on line " << line_; + if (expected != NULL && actual.GetSettings() == NULL) { + return testing::AssertionFailure() << + "Expected: " << GetJSON(*expected) << ", actual NULL"; } - - private: - std::string GetJson(Value* value) { - std::string json; - base::JSONWriter::Write(value, true, &json); - return json; + if (!expected->Equals(actual.GetSettings())) { + return testing::AssertionFailure() << + "Expected: " << GetJSON(*expected) << + ", actual: " << GetJSON(*actual.GetSettings()); } - - DictionaryValue* expected_; - int line_; - bool called_; -}; - -} // namespace + return testing::AssertionSuccess(); +} ExtensionSettingsStorageTest::ExtensionSettingsStorageTest() - : key1_("foo"), key2_("bar"), key3_("baz") { + : key1_("foo"), + key2_("bar"), + key3_("baz"), + empty_dict_(new DictionaryValue), + dict1_(new DictionaryValue), + dict12_(new DictionaryValue), + dict123_(new DictionaryValue) { val1_.reset(Value::CreateStringValue(key1_ + "Value")); val2_.reset(Value::CreateStringValue(key2_ + "Value")); val3_.reset(Value::CreateStringValue(key3_ + "Value")); - emptyList_.reset(new ListValue()); + list1_.push_back(key1_); + list2_.push_back(key2_); + list12_.push_back(key1_); + list12_.push_back(key2_); + list13_.push_back(key1_); + list13_.push_back(key3_); + list123_.push_back(key1_); + list123_.push_back(key2_); + list123_.push_back(key3_); - list1_.reset(new ListValue()); - list1_->Append(Value::CreateStringValue(key1_)); - - list2_.reset(new ListValue()); - list2_->Append(Value::CreateStringValue(key2_)); - - list12_.reset(new ListValue()); - list12_->Append(Value::CreateStringValue(key1_)); - list12_->Append(Value::CreateStringValue(key2_)); - - list13_.reset(new ListValue()); - list13_->Append(Value::CreateStringValue(key1_)); - list13_->Append(Value::CreateStringValue(key3_)); - - list123_.reset(new ListValue()); - list123_->Append(Value::CreateStringValue(key1_)); - list123_->Append(Value::CreateStringValue(key2_)); - list123_->Append(Value::CreateStringValue(key3_)); - - emptyDict_.reset(new DictionaryValue()); - - dict1_.reset(new DictionaryValue()); dict1_->Set(key1_, val1_->DeepCopy()); - - dict12_.reset(new DictionaryValue()); dict12_->Set(key1_, val1_->DeepCopy()); dict12_->Set(key2_, val2_->DeepCopy()); - - dict123_.reset(new DictionaryValue()); dict123_->Set(key1_, val1_->DeepCopy()); dict123_->Set(key2_, val2_->DeepCopy()); dict123_->Set(key3_, val3_->DeepCopy()); } -ExtensionSettingsStorageTest::~ExtensionSettingsStorageTest() { -} +ExtensionSettingsStorageTest::~ExtensionSettingsStorageTest() {} void ExtensionSettingsStorageTest::SetUp() { - ui_message_loop_ = new MessageLoopForUI(); - // Use the same message loop for the UI and FILE threads, giving a test - // pattern where storage API calls get posted to the same message loop (the - // current one), then all run with MessageLoop::current()->RunAllPending(). - ui_thread_ = new BrowserThread(BrowserThread::UI, MessageLoop::current()); - file_thread_ = new BrowserThread(BrowserThread::FILE, MessageLoop::current()); + ui_message_loop_.reset(new MessageLoopForUI()); + ui_thread_.reset( + new BrowserThread(BrowserThread::UI, MessageLoop::current())); + file_thread_.reset( + new BrowserThread(BrowserThread::FILE, MessageLoop::current())); FilePath temp_dir; file_util::CreateNewTempDirectory(FilePath::StringType(), &temp_dir); @@ -139,153 +102,124 @@ void ExtensionSettingsStorageTest::SetUp() { DCHECK(storage_ != NULL); } -void ExtensionSettingsStorageTest::TearDown() { - settings_ = NULL; - delete file_thread_; - delete ui_thread_; - delete ui_message_loop_; -} - void ExtensionSettingsStorageTest::SetStorage( ExtensionSettingsStorage* storage) { storage_ = storage; } TEST_P(ExtensionSettingsStorageTest, GetWhenEmpty) { - storage_->Get(key1_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(key2_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(key3_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*emptyList_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*list1_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*list123_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(NEW_CALLBACK(emptyDict_.get())); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(key1_)); + ASSERT_PRED_FORMAT2(SettingsEq, + empty_dict_.get(), storage_->Get(empty_list_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(list123_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get()); } TEST_P(ExtensionSettingsStorageTest, GetWithSingleValue) { - storage_->Set(key1_, *val1_, NEW_CALLBACK(dict1_.get())); - MessageLoop::current()->RunAllPending(); - - storage_->Get(key1_, NEW_CALLBACK(dict1_.get())); - storage_->Get(key2_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(key3_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*emptyList_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*list1_, NEW_CALLBACK(dict1_.get())); - storage_->Get(*list2_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*list123_, NEW_CALLBACK(dict1_.get())); - storage_->Get(NEW_CALLBACK(dict1_.get())); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, dict1_.get(), storage_->Set(key1_, *val1_)); + + ASSERT_PRED_FORMAT2(SettingsEq, dict1_.get(), storage_->Get(key1_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(key2_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(key3_)); + ASSERT_PRED_FORMAT2(SettingsEq, + empty_dict_.get(), storage_->Get(empty_list_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict1_.get(), storage_->Get(list123_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict1_.get(), storage_->Get()); } TEST_P(ExtensionSettingsStorageTest, GetWithMultipleValues) { - storage_->Set(*dict12_, NEW_CALLBACK(dict12_.get())); - MessageLoop::current()->RunAllPending(); - - storage_->Get(key1_, NEW_CALLBACK(dict1_.get())); - storage_->Get(key3_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*emptyList_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*list1_, NEW_CALLBACK(dict1_.get())); - storage_->Get(*list13_, NEW_CALLBACK(dict1_.get())); - storage_->Get(*list12_, NEW_CALLBACK(dict12_.get())); - storage_->Get(*list123_, NEW_CALLBACK(dict12_.get())); - storage_->Get(NEW_CALLBACK(dict12_.get())); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, dict12_.get(), storage_->Set(*dict12_)); + + ASSERT_PRED_FORMAT2(SettingsEq, dict1_.get(), storage_->Get(key1_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(key3_)); + ASSERT_PRED_FORMAT2(SettingsEq, + empty_dict_.get(), storage_->Get(empty_list_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict12_.get(), storage_->Get(list123_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict12_.get(), storage_->Get()); } TEST_P(ExtensionSettingsStorageTest, RemoveWhenEmpty) { - storage_->Remove(key1_, NEW_CALLBACK(NULL)); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, NULL, storage_->Remove(key1_)); - storage_->Get(key1_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*list1_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(NEW_CALLBACK(emptyDict_.get())); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(key1_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(list1_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get()); } TEST_P(ExtensionSettingsStorageTest, RemoveWithSingleValue) { - storage_->Set(key1_, *val1_, NEW_CALLBACK(dict1_.get())); - MessageLoop::current()->RunAllPending(); - storage_->Remove(key1_, NEW_CALLBACK(NULL)); - MessageLoop::current()->RunAllPending(); - - storage_->Get(key1_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(key2_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*list1_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*list12_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(NEW_CALLBACK(emptyDict_.get())); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, dict1_.get(), storage_->Set(*dict1_)); + ASSERT_PRED_FORMAT2(SettingsEq, NULL, storage_->Remove(key1_)); + + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(key1_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(key2_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(list1_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(list12_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get()); } TEST_P(ExtensionSettingsStorageTest, RemoveWithMultipleValues) { - storage_->Set(*dict123_, NEW_CALLBACK(dict123_.get())); - MessageLoop::current()->RunAllPending(); - storage_->Remove(key3_, NEW_CALLBACK(NULL)); - MessageLoop::current()->RunAllPending(); - - storage_->Get(key1_, NEW_CALLBACK(dict1_.get())); - storage_->Get(key3_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*emptyList_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*list1_, NEW_CALLBACK(dict1_.get())); - storage_->Get(*list13_, NEW_CALLBACK(dict1_.get())); - storage_->Get(*list12_, NEW_CALLBACK(dict12_.get())); - storage_->Get(*list123_, NEW_CALLBACK(dict12_.get())); - storage_->Get(NEW_CALLBACK(dict12_.get())); - - storage_->Remove(*list2_, NEW_CALLBACK(NULL)); - MessageLoop::current()->RunAllPending(); - - storage_->Get(key1_, NEW_CALLBACK(dict1_.get())); - storage_->Get(key2_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(key3_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*emptyList_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*list1_, NEW_CALLBACK(dict1_.get())); - storage_->Get(*list2_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*list123_, NEW_CALLBACK(dict1_.get())); - storage_->Get(NEW_CALLBACK(dict1_.get())); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, dict123_.get(), storage_->Set(*dict123_)); + ASSERT_PRED_FORMAT2(SettingsEq, NULL, storage_->Remove(key3_)); + + ASSERT_PRED_FORMAT2(SettingsEq, dict1_.get(), storage_->Get(key1_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(key3_)); + ASSERT_PRED_FORMAT2(SettingsEq, + empty_dict_.get(), storage_->Get(empty_list_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict1_.get(), storage_->Get(list1_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict12_.get(), storage_->Get(list12_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict1_.get(), storage_->Get(list13_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict12_.get(), storage_->Get(list123_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict12_.get(), storage_->Get()); + + ASSERT_PRED_FORMAT2(SettingsEq, NULL, storage_->Remove(list12_)); + + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(key1_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(key3_)); + ASSERT_PRED_FORMAT2(SettingsEq, + empty_dict_.get(), storage_->Get(empty_list_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(list1_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(list12_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(list13_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(list123_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get()); } TEST_P(ExtensionSettingsStorageTest, SetWhenOverwriting) { - DictionaryValue key1Val2; - key1Val2.Set(key1_, val2_->DeepCopy()); - storage_->Set(key1_, *val2_, NEW_CALLBACK(&key1Val2)); - MessageLoop::current()->RunAllPending(); - - storage_->Set(*dict12_, NEW_CALLBACK(dict12_.get())); - MessageLoop::current()->RunAllPending(); - - storage_->Get(key1_, NEW_CALLBACK(dict1_.get())); - storage_->Get(key3_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*emptyList_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*list1_, NEW_CALLBACK(dict1_.get())); - storage_->Get(*list13_, NEW_CALLBACK(dict1_.get())); - storage_->Get(*list12_, NEW_CALLBACK(dict12_.get())); - storage_->Get(*list123_, NEW_CALLBACK(dict12_.get())); - storage_->Get(NEW_CALLBACK(dict12_.get())); - MessageLoop::current()->RunAllPending(); + DictionaryValue key1_val2; + key1_val2.Set(key1_, val2_->DeepCopy()); + ASSERT_PRED_FORMAT2(SettingsEq, &key1_val2, storage_->Set(key1_, *val2_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict12_.get(), storage_->Set(*dict12_)); + + ASSERT_PRED_FORMAT2(SettingsEq, dict1_.get(), storage_->Get(key1_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(key3_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), + storage_->Get(empty_list_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict1_.get(), storage_->Get(list1_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict12_.get(), storage_->Get(list12_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict1_.get(), storage_->Get(list13_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict12_.get(), storage_->Get(list123_)); + ASSERT_PRED_FORMAT2(SettingsEq, dict12_.get(), storage_->Get()); } TEST_P(ExtensionSettingsStorageTest, ClearWhenEmpty) { - storage_->Clear(NEW_CALLBACK(NULL)); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, NULL, storage_->Clear()); - storage_->Get(key1_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*list1_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(NEW_CALLBACK(emptyDict_.get())); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(key1_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), + storage_->Get(empty_list_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(list123_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get()); } TEST_P(ExtensionSettingsStorageTest, ClearWhenNotEmpty) { - storage_->Set(*dict12_, NEW_CALLBACK(dict12_.get())); - MessageLoop::current()->RunAllPending(); - - storage_->Clear(NEW_CALLBACK(NULL)); - MessageLoop::current()->RunAllPending(); - - storage_->Get(key1_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(*list1_, NEW_CALLBACK(emptyDict_.get())); - storage_->Get(NEW_CALLBACK(emptyDict_.get())); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, dict12_.get(), storage_->Set(*dict12_)); + ASSERT_PRED_FORMAT2(SettingsEq, NULL, storage_->Clear()); + + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(key1_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), + storage_->Get(empty_list_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(list123_)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get()); } // Dots should be allowed in key names; they shouldn't be interpreted as @@ -293,31 +227,24 @@ TEST_P(ExtensionSettingsStorageTest, ClearWhenNotEmpty) { TEST_P(ExtensionSettingsStorageTest, DotsInKeyNames) { std::string dot_key("foo.bar"); StringValue dot_value("baz.qux"); - ListValue dot_list; - dot_list.Append(Value::CreateStringValue(dot_key)); + std::vector<std::string> dot_list; + dot_list.push_back(dot_key); DictionaryValue dot_dict; dot_dict.SetWithoutPathExpansion(dot_key, dot_value.DeepCopy()); - storage_->Set(dot_key, dot_value, NEW_CALLBACK(&dot_dict)); - MessageLoop::current()->RunAllPending(); - - storage_->Get(dot_key, NEW_CALLBACK(&dot_dict)); - MessageLoop::current()->RunAllPending(); - - storage_->Remove(dot_key, NEW_CALLBACK(NULL)); - MessageLoop::current()->RunAllPending(); - - storage_->Set(dot_dict, NEW_CALLBACK(&dot_dict)); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(dot_key)); - storage_->Get(dot_list, NEW_CALLBACK(&dot_dict)); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, &dot_dict, storage_->Set(dot_key, dot_value)); + ASSERT_PRED_FORMAT2(SettingsEq, &dot_dict, storage_->Get(dot_key)); - storage_->Remove(dot_list, NEW_CALLBACK(NULL)); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, NULL, storage_->Remove(dot_key)); + ASSERT_PRED_FORMAT2(SettingsEq, &dot_dict, storage_->Set(dot_dict)); + ASSERT_PRED_FORMAT2(SettingsEq, &dot_dict, storage_->Get(dot_list)); + ASSERT_PRED_FORMAT2(SettingsEq, &dot_dict, storage_->Get()); - storage_->Get(NEW_CALLBACK(emptyDict_.get())); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, NULL, storage_->Remove(dot_list)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get(dot_key)); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get()); } TEST_P(ExtensionSettingsStorageTest, DotsInKeyNamesWithDicts) { @@ -326,12 +253,7 @@ TEST_P(ExtensionSettingsStorageTest, DotsInKeyNamesWithDicts) { outer_dict.Set("foo", inner_dict); inner_dict->Set("bar", Value::CreateStringValue("baz")); - storage_->Set(outer_dict, NEW_CALLBACK(&outer_dict)); - MessageLoop::current()->RunAllPending(); - - storage_->Get("foo", NEW_CALLBACK(&outer_dict)); - MessageLoop::current()->RunAllPending(); - - storage_->Get("foo.bar", NEW_CALLBACK(emptyDict_.get())); - MessageLoop::current()->RunAllPending(); + ASSERT_PRED_FORMAT2(SettingsEq, &outer_dict, storage_->Set(outer_dict)); + ASSERT_PRED_FORMAT2(SettingsEq, &outer_dict, storage_->Get("foo")); + ASSERT_PRED_FORMAT2(SettingsEq, empty_dict_.get(), storage_->Get("foo.bar")); } diff --git a/chrome/browser/extensions/extension_settings_storage_unittest.h b/chrome/browser/extensions/extension_settings_storage_unittest.h index 4fac5af..e13c80a 100644 --- a/chrome/browser/extensions/extension_settings_storage_unittest.h +++ b/chrome/browser/extensions/extension_settings_storage_unittest.h @@ -31,7 +31,6 @@ class ExtensionSettingsStorageTest virtual ~ExtensionSettingsStorageTest(); virtual void SetUp() OVERRIDE; - virtual void TearDown() OVERRIDE; protected: ExtensionSettingsStorage* storage_; @@ -39,16 +38,19 @@ class ExtensionSettingsStorageTest std::string key1_; std::string key2_; std::string key3_; + scoped_ptr<Value> val1_; scoped_ptr<Value> val2_; scoped_ptr<Value> val3_; - scoped_ptr<ListValue> emptyList_; - scoped_ptr<ListValue> list1_; - scoped_ptr<ListValue> list2_; - scoped_ptr<ListValue> list12_; - scoped_ptr<ListValue> list13_; - scoped_ptr<ListValue> list123_; - scoped_ptr<DictionaryValue> emptyDict_; + + std::vector<std::string> empty_list_; + std::vector<std::string> list1_; + std::vector<std::string> list2_; + std::vector<std::string> list12_; + std::vector<std::string> list13_; + std::vector<std::string> list123_; + + scoped_ptr<DictionaryValue> empty_dict_; scoped_ptr<DictionaryValue> dict1_; scoped_ptr<DictionaryValue> dict12_; scoped_ptr<DictionaryValue> dict123_; @@ -57,9 +59,9 @@ class ExtensionSettingsStorageTest void SetStorage(ExtensionSettingsStorage* storage); scoped_refptr<ExtensionSettings> settings_; - MessageLoopForUI* ui_message_loop_; - BrowserThread* ui_thread_; - BrowserThread* file_thread_; + scoped_ptr<MessageLoopForUI> ui_message_loop_; + scoped_ptr<BrowserThread> ui_thread_; + scoped_ptr<BrowserThread> file_thread_; }; #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_STORAGE_UNITTEST_H_ diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 2f0454f..1bd0b95 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1073,6 +1073,7 @@ 'browser/extensions/extension_settings_api.h', 'browser/extensions/extension_settings_noop_storage.cc', 'browser/extensions/extension_settings_noop_storage.h', + 'browser/extensions/extension_settings_storage.cc', 'browser/extensions/extension_settings_storage.h', 'browser/extensions/extension_settings_storage_cache.cc', 'browser/extensions/extension_settings_storage_cache.h', |