summaryrefslogtreecommitdiffstats
path: root/extensions/browser/api
diff options
context:
space:
mode:
authorjamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-13 19:57:41 +0000
committerjamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-13 19:57:41 +0000
commit3a4f74af6084db9d35b298f889d00557445e8bab (patch)
treed784934185d8a9aa82d728191375c4863ee3ab4b /extensions/browser/api
parent2f018d6f8564359c10abf57999a787142713c631 (diff)
downloadchromium_src-3a4f74af6084db9d35b298f889d00557445e8bab.zip
chromium_src-3a4f74af6084db9d35b298f889d00557445e8bab.tar.gz
chromium_src-3a4f74af6084db9d35b298f889d00557445e8bab.tar.bz2
Move storage API interface description to src/extensions
The extensions local-storage API is moving into src/extensions so it can be used by app_shell. This finishes that move. * Move storage.json to extensions/common/api * Move storage functions into core_api namespace * Remove ExtensionService references from StorageApiUnittest BUG=348058 TEST=unit_tests *Storage* and browser_tests *Storage* Review URL: https://codereview.chromium.org/197883002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@256901 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions/browser/api')
-rw-r--r--extensions/browser/api/storage/local_value_store_cache.cc6
-rw-r--r--extensions/browser/api/storage/settings_frontend.cc4
-rw-r--r--extensions/browser/api/storage/storage_api.cc304
-rw-r--r--extensions/browser/api/storage/storage_api.h143
-rw-r--r--extensions/browser/api/storage/storage_api_unittest.cc117
5 files changed, 569 insertions, 5 deletions
diff --git a/extensions/browser/api/storage/local_value_store_cache.cc b/extensions/browser/api/storage/local_value_store_cache.cc
index 7c7ce58..6d05ca2 100644
--- a/extensions/browser/api/storage/local_value_store_cache.cc
+++ b/extensions/browser/api/storage/local_value_store_cache.cc
@@ -9,12 +9,12 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_path.h"
-#include "chrome/common/extensions/api/storage.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/api/storage/settings_storage_factory.h"
#include "extensions/browser/api/storage/settings_storage_quota_enforcer.h"
#include "extensions/browser/api/storage/weak_unlimited_settings_storage.h"
#include "extensions/browser/value_store/value_store.h"
+#include "extensions/common/api/storage.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/permissions/api_permission.h"
@@ -26,10 +26,10 @@ namespace extensions {
namespace {
// Returns the quota limit for local storage, taken from the schema in
-// chrome/common/extensions/api/storage.json.
+// extensions/common/api/storage.json.
SettingsStorageQuotaEnforcer::Limits GetLocalQuotaLimits() {
SettingsStorageQuotaEnforcer::Limits limits = {
- static_cast<size_t>(api::storage::local::QUOTA_BYTES),
+ static_cast<size_t>(core_api::storage::local::QUOTA_BYTES),
std::numeric_limits<size_t>::max(),
std::numeric_limits<size_t>::max()
};
diff --git a/extensions/browser/api/storage/settings_frontend.cc b/extensions/browser/api/storage/settings_frontend.cc
index 6fa3337..87dc8e9 100644
--- a/extensions/browser/api/storage/settings_frontend.cc
+++ b/extensions/browser/api/storage/settings_frontend.cc
@@ -9,7 +9,6 @@
#include "base/files/file_path.h"
#include "base/json/json_reader.h"
#include "base/lazy_instance.h"
-#include "chrome/common/extensions/api/storage.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/api/extensions_api_client.h"
@@ -18,6 +17,7 @@
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
+#include "extensions/common/api/storage.h"
using content::BrowserContext;
using content::BrowserThread;
@@ -48,7 +48,7 @@ class DefaultObserver : public SettingsObserver {
args->Append(new base::StringValue(settings_namespace::ToString(
settings_namespace)));
scoped_ptr<Event> event(new Event(
- api::storage::OnChanged::kEventName, args.Pass()));
+ core_api::storage::OnChanged::kEventName, args.Pass()));
ExtensionSystem::Get(browser_context_)->event_router()->
DispatchEventToExtension(extension_id, event.Pass());
}
diff --git a/extensions/browser/api/storage/storage_api.cc b/extensions/browser/api/storage/storage_api.cc
new file mode 100644
index 0000000..939a75a
--- /dev/null
+++ b/extensions/browser/api/storage/storage_api.cc
@@ -0,0 +1,304 @@
+// Copyright 2014 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 "extensions/browser/api/storage/storage_api.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "content/public/browser/browser_thread.h"
+#include "extensions/browser/api/storage/settings_frontend.h"
+#include "extensions/browser/quota_service.h"
+#include "extensions/common/api/storage.h"
+
+namespace extensions {
+
+using content::BrowserThread;
+
+// SettingsFunction
+
+SettingsFunction::SettingsFunction()
+ : settings_namespace_(settings_namespace::INVALID),
+ tried_restoring_storage_(false) {}
+
+SettingsFunction::~SettingsFunction() {}
+
+bool SettingsFunction::ShouldSkipQuotaLimiting() const {
+ // Only apply quota if this is for sync storage.
+ std::string settings_namespace_string;
+ if (!args_->GetString(0, &settings_namespace_string)) {
+ // This should be EXTENSION_FUNCTION_VALIDATE(false) but there is no way
+ // to signify that from this function. It will be caught in RunImpl().
+ return false;
+ }
+ return settings_namespace_string != "sync";
+}
+
+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 = SettingsFrontend::Get(browser_context());
+ if (!frontend->IsStorageEnabled(settings_namespace_)) {
+ error_ = base::StringPrintf(
+ "\"%s\" is not available in this instance of Chrome",
+ settings_namespace_string.c_str());
+ return false;
+ }
+
+ observers_ = frontend->GetObservers();
+ frontend->RunWithStorage(
+ GetExtension(),
+ settings_namespace_,
+ base::Bind(&SettingsFunction::AsyncRunWithStorage, this));
+ return true;
+}
+
+void SettingsFunction::AsyncRunWithStorage(ValueStore* storage) {
+ bool success = RunWithStorage(storage);
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&SettingsFunction::SendResponse, this, success));
+}
+
+bool SettingsFunction::UseReadResult(ValueStore::ReadResult result,
+ ValueStore* storage) {
+ if (result->HasError())
+ return HandleError(result->error(), storage);
+
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->Swap(&result->settings());
+ SetResult(dict);
+ return true;
+}
+
+bool SettingsFunction::UseWriteResult(ValueStore::WriteResult result,
+ ValueStore* storage) {
+ if (result->HasError())
+ return HandleError(result->error(), storage);
+
+ if (!result->changes().empty()) {
+ observers_->Notify(
+ &SettingsObserver::OnSettingsChanged,
+ extension_id(),
+ settings_namespace_,
+ ValueStoreChange::ToJson(result->changes()));
+ }
+
+ return true;
+}
+
+bool SettingsFunction::HandleError(const ValueStore::Error& error,
+ ValueStore* storage) {
+ // If the method failed due to corruption, and we haven't tried to fix it, we
+ // can try to restore the storage and re-run it. Otherwise, the method has
+ // failed.
+ if (error.code == ValueStore::CORRUPTION && !tried_restoring_storage_) {
+ tried_restoring_storage_ = true;
+
+ // If the corruption is on a particular key, try to restore that key and
+ // re-run.
+ if (error.key.get() && storage->RestoreKey(*error.key))
+ return RunWithStorage(storage);
+
+ // If the full database is corrupted, try to restore the whole thing and
+ // re-run.
+ if (storage->Restore())
+ return RunWithStorage(storage);
+ }
+
+ error_ = error.message;
+ return false;
+}
+
+// Concrete settings functions
+
+namespace {
+
+// Adds all StringValues from a ListValue to a vector of strings.
+void AddAllStringValues(const base::ListValue& from,
+ std::vector<std::string>* to) {
+ DCHECK(to->empty());
+ std::string as_string;
+ for (base::ListValue::const_iterator it = from.begin();
+ it != from.end(); ++it) {
+ if ((*it)->GetAsString(&as_string)) {
+ to->push_back(as_string);
+ }
+ }
+}
+
+// Gets the keys of a DictionaryValue.
+std::vector<std::string> GetKeys(const base::DictionaryValue& dict) {
+ std::vector<std::string> keys;
+ for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
+ keys.push_back(it.key());
+ }
+ return keys;
+}
+
+// Creates quota heuristics for settings modification.
+void GetModificationQuotaLimitHeuristics(QuotaLimitHeuristics* heuristics) {
+ QuotaLimitHeuristic::Config longLimitConfig = {
+ // See storage.json for current value.
+ core_api::storage::sync::MAX_WRITE_OPERATIONS_PER_HOUR,
+ base::TimeDelta::FromHours(1)
+ };
+ heuristics->push_back(new QuotaService::TimedLimit(
+ longLimitConfig,
+ new QuotaLimitHeuristic::SingletonBucketMapper(),
+ "MAX_WRITE_OPERATIONS_PER_HOUR"));
+
+ // A max of 10 operations per minute, sustained over 10 minutes.
+ QuotaLimitHeuristic::Config shortLimitConfig = {
+ // See storage.json for current value.
+ core_api::storage::sync::MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE,
+ base::TimeDelta::FromMinutes(1)
+ };
+ heuristics->push_back(new QuotaService::SustainedLimit(
+ base::TimeDelta::FromMinutes(10),
+ shortLimitConfig,
+ new QuotaLimitHeuristic::SingletonBucketMapper(),
+ "MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE"));
+};
+
+} // namespace
+
+bool StorageStorageAreaGetFunction::RunWithStorage(ValueStore* storage) {
+ base::Value* input = NULL;
+ EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input));
+
+ switch (input->GetType()) {
+ case base::Value::TYPE_NULL:
+ return UseReadResult(storage->Get(), storage);
+
+ case base::Value::TYPE_STRING: {
+ std::string as_string;
+ input->GetAsString(&as_string);
+ return UseReadResult(storage->Get(as_string), storage);
+ }
+
+ case base::Value::TYPE_LIST: {
+ std::vector<std::string> as_string_list;
+ AddAllStringValues(*static_cast<base::ListValue*>(input),
+ &as_string_list);
+ return UseReadResult(storage->Get(as_string_list), storage);
+ }
+
+ case base::Value::TYPE_DICTIONARY: {
+ base::DictionaryValue* as_dict =
+ static_cast<base::DictionaryValue*>(input);
+ ValueStore::ReadResult result = storage->Get(GetKeys(*as_dict));
+ if (result->HasError()) {
+ return UseReadResult(result.Pass(), storage);
+ }
+
+ base::DictionaryValue* with_default_values = as_dict->DeepCopy();
+ with_default_values->MergeDictionary(&result->settings());
+ return UseReadResult(
+ ValueStore::MakeReadResult(make_scoped_ptr(with_default_values)),
+ storage);
+ }
+
+ default:
+ EXTENSION_FUNCTION_VALIDATE(false);
+ return false;
+ }
+}
+
+bool StorageStorageAreaGetBytesInUseFunction::RunWithStorage(
+ ValueStore* storage) {
+ base::Value* input = NULL;
+ EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input));
+
+ size_t bytes_in_use = 0;
+
+ switch (input->GetType()) {
+ case base::Value::TYPE_NULL:
+ bytes_in_use = storage->GetBytesInUse();
+ break;
+
+ case base::Value::TYPE_STRING: {
+ std::string as_string;
+ input->GetAsString(&as_string);
+ bytes_in_use = storage->GetBytesInUse(as_string);
+ break;
+ }
+
+ case base::Value::TYPE_LIST: {
+ std::vector<std::string> as_string_list;
+ AddAllStringValues(*static_cast<base::ListValue*>(input),
+ &as_string_list);
+ bytes_in_use = storage->GetBytesInUse(as_string_list);
+ break;
+ }
+
+ default:
+ EXTENSION_FUNCTION_VALIDATE(false);
+ return false;
+ }
+
+ SetResult(new base::FundamentalValue(static_cast<int>(bytes_in_use)));
+ return true;
+}
+
+bool StorageStorageAreaSetFunction::RunWithStorage(ValueStore* storage) {
+ base::DictionaryValue* input = NULL;
+ EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &input));
+ return UseWriteResult(storage->Set(ValueStore::DEFAULTS, *input), storage);
+}
+
+void StorageStorageAreaSetFunction::GetQuotaLimitHeuristics(
+ QuotaLimitHeuristics* heuristics) const {
+ GetModificationQuotaLimitHeuristics(heuristics);
+}
+
+bool StorageStorageAreaRemoveFunction::RunWithStorage(ValueStore* storage) {
+ base::Value* input = NULL;
+ EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input));
+
+ switch (input->GetType()) {
+ case base::Value::TYPE_STRING: {
+ std::string as_string;
+ input->GetAsString(&as_string);
+ return UseWriteResult(storage->Remove(as_string), storage);
+ }
+
+ case base::Value::TYPE_LIST: {
+ std::vector<std::string> as_string_list;
+ AddAllStringValues(*static_cast<base::ListValue*>(input),
+ &as_string_list);
+ return UseWriteResult(storage->Remove(as_string_list), storage);
+ }
+
+ default:
+ EXTENSION_FUNCTION_VALIDATE(false);
+ return false;
+ };
+}
+
+void StorageStorageAreaRemoveFunction::GetQuotaLimitHeuristics(
+ QuotaLimitHeuristics* heuristics) const {
+ GetModificationQuotaLimitHeuristics(heuristics);
+}
+
+bool StorageStorageAreaClearFunction::RunWithStorage(ValueStore* storage) {
+ return UseWriteResult(storage->Clear(), storage);
+}
+
+void StorageStorageAreaClearFunction::GetQuotaLimitHeuristics(
+ QuotaLimitHeuristics* heuristics) const {
+ GetModificationQuotaLimitHeuristics(heuristics);
+}
+
+} // namespace extensions
diff --git a/extensions/browser/api/storage/storage_api.h b/extensions/browser/api/storage/storage_api.h
new file mode 100644
index 0000000..f573235
--- /dev/null
+++ b/extensions/browser/api/storage/storage_api.h
@@ -0,0 +1,143 @@
+// Copyright 2014 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 EXTENSIONS_BROWSER_API_STORAGE_STORAGE_API_H_
+#define EXTENSIONS_BROWSER_API_STORAGE_STORAGE_API_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "extensions/browser/api/storage/settings_namespace.h"
+#include "extensions/browser/api/storage/settings_observer.h"
+#include "extensions/browser/extension_function.h"
+#include "extensions/browser/value_store/value_store.h"
+
+namespace extensions {
+
+// Superclass of all settings functions.
+class SettingsFunction : public UIThreadExtensionFunction {
+ protected:
+ SettingsFunction();
+ virtual ~SettingsFunction();
+
+ // ExtensionFunction:
+ virtual bool ShouldSkipQuotaLimiting() const OVERRIDE;
+ virtual bool RunImpl() OVERRIDE;
+
+ // Extension settings function implementations should do their work here.
+ // The SettingsFrontend makes sure this is posted to the appropriate thread.
+ // Implementations should fill in args themselves, though (like RunImpl)
+ // may return false to imply failure.
+ virtual bool RunWithStorage(ValueStore* storage) = 0;
+
+ // Handles the |result| of a read function.
+ // - If the result succeeded, this will set |result_| and return.
+ // - If |result| failed with a ValueStore::CORRUPTION error, this will call
+ // RestoreStorageAndRetry(), and return that result.
+ // - If the |result| failed with a different error, this will set |error_|
+ // and return.
+ bool UseReadResult(ValueStore::ReadResult result, ValueStore* storage);
+
+ // Handles the |result| of a write function.
+ // - If the result succeeded, this will set |result_| and return.
+ // - If |result| failed with a ValueStore::CORRUPTION error, this will call
+ // RestoreStorageAndRetry(), and return that result.
+ // - If the |result| failed with a different error, this will set |error_|
+ // and return.
+ // This will also send out a change notification, if appropriate.
+ bool UseWriteResult(ValueStore::WriteResult result, ValueStore* storage);
+
+ private:
+ // Called via PostTask from RunImpl. Calls RunWithStorage and then
+ // SendResponse with its success value.
+ void AsyncRunWithStorage(ValueStore* storage);
+
+ // Called if we encounter a ValueStore error. If the error is due to
+ // corruption, tries to restore the ValueStore and re-run the API function.
+ // If the storage cannot be restored or was due to some other error, then sets
+ // error and returns. This also sets the |tried_restoring_storage_| flag to
+ // ensure we don't enter a loop.
+ bool HandleError(const ValueStore::Error& error, ValueStore* 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_;
+
+ // A flag indicating whether or not we have tried to restore storage. We
+ // should only ever try once (per API call) in order to avoid entering a loop.
+ bool tried_restoring_storage_;
+
+ // Observers, cached so that it's only grabbed from the UI thread.
+ scoped_refptr<SettingsObserverList> observers_;
+};
+
+class StorageStorageAreaGetFunction : public SettingsFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("storage.get", STORAGE_GET)
+
+ protected:
+ virtual ~StorageStorageAreaGetFunction() {}
+
+ // SettingsFunction:
+ virtual bool RunWithStorage(ValueStore* storage) OVERRIDE;
+};
+
+class StorageStorageAreaSetFunction : public SettingsFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("storage.set", STORAGE_SET)
+
+ protected:
+ virtual ~StorageStorageAreaSetFunction() {}
+
+ // SettingsFunction:
+ virtual bool RunWithStorage(ValueStore* storage) OVERRIDE;
+
+ // ExtensionFunction:
+ virtual void GetQuotaLimitHeuristics(
+ QuotaLimitHeuristics* heuristics) const OVERRIDE;
+};
+
+class StorageStorageAreaRemoveFunction : public SettingsFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("storage.remove", STORAGE_REMOVE)
+
+ protected:
+ virtual ~StorageStorageAreaRemoveFunction() {}
+
+ // SettingsFunction:
+ virtual bool RunWithStorage(ValueStore* storage) OVERRIDE;
+
+ // ExtensionFunction:
+ virtual void GetQuotaLimitHeuristics(
+ QuotaLimitHeuristics* heuristics) const OVERRIDE;
+};
+
+class StorageStorageAreaClearFunction : public SettingsFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("storage.clear", STORAGE_CLEAR)
+
+ protected:
+ virtual ~StorageStorageAreaClearFunction() {}
+
+ // SettingsFunction:
+ virtual bool RunWithStorage(ValueStore* storage) OVERRIDE;
+
+ // ExtensionFunction:
+ virtual void GetQuotaLimitHeuristics(
+ QuotaLimitHeuristics* heuristics) const OVERRIDE;
+};
+
+class StorageStorageAreaGetBytesInUseFunction : public SettingsFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("storage.getBytesInUse", STORAGE_GETBYTESINUSE)
+
+ protected:
+ virtual ~StorageStorageAreaGetBytesInUseFunction() {}
+
+ // SettingsFunction:
+ virtual bool RunWithStorage(ValueStore* storage) OVERRIDE;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_API_STORAGE_STORAGE_API_H_
diff --git a/extensions/browser/api/storage/storage_api_unittest.cc b/extensions/browser/api/storage/storage_api_unittest.cc
new file mode 100644
index 0000000..2fe5269
--- /dev/null
+++ b/extensions/browser/api/storage/storage_api_unittest.cc
@@ -0,0 +1,117 @@
+// Copyright 2014 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/command_line.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/extensions/extension_api_unittest.h"
+#include "chrome/browser/extensions/test_extension_system.h"
+#include "extensions/browser/api/storage/leveldb_settings_storage_factory.h"
+#include "extensions/browser/api/storage/settings_frontend.h"
+#include "extensions/browser/api/storage/settings_storage_quota_enforcer.h"
+#include "extensions/browser/api/storage/settings_test_util.h"
+#include "extensions/browser/api/storage/storage_api.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/value_store/leveldb_value_store.h"
+#include "extensions/browser/value_store/value_store.h"
+#include "extensions/common/id_util.h"
+#include "extensions/common/manifest.h"
+#include "extensions/common/test_util.h"
+#include "third_party/leveldatabase/src/include/leveldb/db.h"
+#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
+
+namespace extensions {
+
+namespace {
+
+// Caller owns the returned object.
+KeyedService* CreateSettingsFrontendForTesting(
+ content::BrowserContext* context) {
+ return SettingsFrontend::CreateForTesting(new LeveldbSettingsStorageFactory(),
+ context);
+}
+
+} // namespace
+
+class StorageApiUnittest : public ExtensionApiUnittest {
+ public:
+ virtual void SetUp() OVERRIDE {
+ ExtensionApiUnittest::SetUp();
+ TestExtensionSystem* extension_system =
+ static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()));
+ // SettingsFrontend requires an EventRouter.
+ extension_system->SetEventRouter(scoped_ptr<EventRouter>(
+ new EventRouter(profile(), ExtensionPrefs::Get(profile()))));
+ }
+
+ protected:
+ // Runs the storage.set() API function with local storage.
+ void RunSetFunction(const std::string& key, const std::string& value) {
+ RunFunction(
+ new StorageStorageAreaSetFunction(),
+ base::StringPrintf(
+ "[\"local\", {\"%s\": \"%s\"}]", key.c_str(), value.c_str()));
+ }
+
+ // Runs the storage.get() API function with the local storage, and populates
+ // |value| with the string result.
+ testing::AssertionResult RunGetFunction(const std::string& key,
+ std::string* value) {
+ scoped_ptr<base::Value> result = RunFunctionAndReturnValue(
+ new StorageStorageAreaGetFunction(),
+ base::StringPrintf("[\"local\", \"%s\"]", key.c_str()));
+ base::DictionaryValue* dict = NULL;
+ if (!result->GetAsDictionary(&dict))
+ return testing::AssertionFailure() << result << " was not a dictionary.";
+ if (!dict->GetString(key, value)) {
+ return testing::AssertionFailure() << " could not retrieve a string from"
+ << dict << " at " << key;
+ }
+ return testing::AssertionSuccess();
+ }
+};
+
+TEST_F(StorageApiUnittest, RestoreCorruptedStorage) {
+ // Ensure a SettingsFrontend can be created on demand. The SettingsFrontend
+ // will be owned by the KeyedService system.
+ SettingsFrontend::GetFactoryInstance()->SetTestingFactory(
+ profile(), &CreateSettingsFrontendForTesting);
+
+ const char kKey[] = "key";
+ const char kValue[] = "value";
+ std::string result;
+
+ // Do a simple set/get combo to make sure the API works.
+ RunSetFunction(kKey, kValue);
+ EXPECT_TRUE(RunGetFunction(kKey, &result));
+ EXPECT_EQ(kValue, result);
+
+ // Corrupt the store. This is not as pretty as ideal, because we use knowledge
+ // of the underlying structure, but there's no real good way to corrupt a
+ // store other than directly modifying the files.
+ ValueStore* store =
+ settings_test_util::GetStorage(extension_ref(),
+ settings_namespace::LOCAL,
+ SettingsFrontend::Get(profile()));
+ ASSERT_TRUE(store);
+ SettingsStorageQuotaEnforcer* quota_store =
+ static_cast<SettingsStorageQuotaEnforcer*>(store);
+ LeveldbValueStore* leveldb_store =
+ static_cast<LeveldbValueStore*>(quota_store->get_delegate_for_test());
+ leveldb::WriteBatch batch;
+ batch.Put(kKey, "[{(.*+\"\'\\");
+ EXPECT_TRUE(leveldb_store->WriteToDbForTest(&batch));
+ EXPECT_TRUE(leveldb_store->Get(kKey)->IsCorrupted());
+
+ // Running another set should end up working (even though it will restore the
+ // store behind the scenes).
+ RunSetFunction(kKey, kValue);
+ EXPECT_TRUE(RunGetFunction(kKey, &result));
+ EXPECT_EQ(kValue, result);
+}
+
+} // namespace extensions