summaryrefslogtreecommitdiffstats
path: root/webkit/dom_storage
diff options
context:
space:
mode:
authormichaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-24 22:56:50 +0000
committermichaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-24 22:56:50 +0000
commitbc4297aad8940a1d13d92f7d71c0ee6d1c8c7aaa (patch)
tree59af1a81ebc62f24c7b458c92fbd1c99aee496c2 /webkit/dom_storage
parent7a8d1ed4bc596d42a468f7c41776b4f433c4e5a0 (diff)
downloadchromium_src-bc4297aad8940a1d13d92f7d71c0ee6d1c8c7aaa.zip
chromium_src-bc4297aad8940a1d13d92f7d71c0ee6d1c8c7aaa.tar.gz
chromium_src-bc4297aad8940a1d13d92f7d71c0ee6d1c8c7aaa.tar.bz2
DomStorageCachedArea + DomStorageProxy interface and unittests. These classes aren't used yet.
BUG=94382 Review URL: https://chromiumcodereview.appspot.com/10450009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@138921 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/dom_storage')
-rw-r--r--webkit/dom_storage/dom_storage_cached_area.cc194
-rw-r--r--webkit/dom_storage/dom_storage_cached_area.h87
-rw-r--r--webkit/dom_storage/dom_storage_cached_area_unittest.cc349
-rw-r--r--webkit/dom_storage/dom_storage_map.h4
-rw-r--r--webkit/dom_storage/dom_storage_proxy.h44
-rw-r--r--webkit/dom_storage/webkit_dom_storage.gypi3
6 files changed, 680 insertions, 1 deletions
diff --git a/webkit/dom_storage/dom_storage_cached_area.cc b/webkit/dom_storage/dom_storage_cached_area.cc
new file mode 100644
index 0000000..6dfd1e5
--- /dev/null
+++ b/webkit/dom_storage/dom_storage_cached_area.cc
@@ -0,0 +1,194 @@
+// Copyright (c) 2012 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 "webkit/dom_storage/dom_storage_cached_area.h"
+
+#include "base/basictypes.h"
+#include "webkit/dom_storage/dom_storage_map.h"
+#include "webkit/dom_storage/dom_storage_proxy.h"
+
+namespace dom_storage {
+
+DomStorageCachedArea::DomStorageCachedArea(
+ int64 namespace_id, const GURL& origin, DomStorageProxy* proxy)
+ : ignore_all_mutations_(false),
+ namespace_id_(namespace_id), origin_(origin),
+ proxy_(proxy), weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+}
+
+DomStorageCachedArea::~DomStorageCachedArea() {
+}
+
+unsigned DomStorageCachedArea::GetLength(int connection_id) {
+ PrimeIfNeeded(connection_id);
+ return map_->Length();
+}
+
+NullableString16 DomStorageCachedArea::GetKey(
+ int connection_id, unsigned index) {
+ PrimeIfNeeded(connection_id);
+ return map_->Key(index);
+}
+
+NullableString16 DomStorageCachedArea::GetItem(
+ int connection_id, const string16& key) {
+ PrimeIfNeeded(connection_id);
+ return map_->GetItem(key);
+}
+
+bool DomStorageCachedArea::SetItem(
+ int connection_id, const string16& key,
+ const string16& value, const GURL& page_url) {
+ // A quick check to reject obviously overbudget items to avoid
+ // the priming the cache.
+ if (key.length() + value.length() > dom_storage::kPerAreaQuota)
+ return false;
+
+ PrimeIfNeeded(connection_id);
+ NullableString16 unused;
+ if (!map_->SetItem(key, value, &unused))
+ return false;
+
+ // Ignore mutations to 'key' until OnSetItemComplete.
+ ignore_key_mutations_[key]++;
+ proxy_->SetItem(
+ connection_id, key, value, page_url,
+ base::Bind(&DomStorageCachedArea::OnSetItemComplete,
+ weak_factory_.GetWeakPtr(), key));
+ return true;
+}
+
+void DomStorageCachedArea::RemoveItem(
+ int connection_id, const string16& key, const GURL& page_url) {
+ PrimeIfNeeded(connection_id);
+ string16 unused;
+ if (!map_->RemoveItem(key, &unused))
+ return;
+
+ // Ignore mutations to 'key' until OnRemoveItemComplete.
+ ignore_key_mutations_[key]++;
+ proxy_->RemoveItem(
+ connection_id, key, page_url,
+ base::Bind(&DomStorageCachedArea::OnRemoveItemComplete,
+ weak_factory_.GetWeakPtr(), key));
+}
+
+void DomStorageCachedArea::Clear(int connection_id, const GURL& page_url) {
+ // No need to prime the cache in this case.
+ Reset();
+ map_ = new DomStorageMap(dom_storage::kPerAreaQuota);
+
+ // Ignore all mutations until OnClearComplete time.
+ ignore_all_mutations_ = true;
+ proxy_->ClearArea(
+ connection_id, page_url,
+ base::Bind(&DomStorageCachedArea::OnClearComplete,
+ weak_factory_.GetWeakPtr()));
+}
+
+void DomStorageCachedArea::ApplyMutation(
+ const NullableString16& key, const NullableString16& new_value) {
+ if (!map_ || ignore_all_mutations_)
+ return;
+
+ if (key.is_null()) {
+ // It's a clear event.
+ scoped_refptr<DomStorageMap> old = map_;
+ map_ = new DomStorageMap(dom_storage::kPerAreaQuota);
+
+ // We have to retain local additions which happened after this
+ // clear operation from another process.
+ std::map<string16, int>::iterator iter = ignore_key_mutations_.begin();
+ while (iter != ignore_key_mutations_.end()) {
+ NullableString16 value = old->GetItem(iter->first);
+ if (!value.is_null()) {
+ NullableString16 unused;
+ map_->SetItem(iter->first, value.string(), &unused);
+ }
+ ++iter;
+ }
+ return;
+ }
+
+ // We have to retain local changes.
+ if (should_ignore_key_mutation(key.string()))
+ return;
+
+ if (new_value.is_null()) {
+ // It's a remove item event.
+ string16 unused;
+ map_->RemoveItem(key.string(), &unused);
+ return;
+ }
+
+ // It's a set item event.
+ // We turn off quota checking here to accomodate the over budget
+ // allowance that's provided in the browser process.
+ NullableString16 unused;
+ map_->set_quota(kint32max);
+ map_->SetItem(key.string(), new_value.string(), &unused);
+ map_->set_quota(dom_storage::kPerAreaQuota);
+}
+
+void DomStorageCachedArea::Prime(int connection_id) {
+ DCHECK(!map_);
+
+ // The LoadArea method is actually synchronous, but we have to
+ // wait for an asyncly delivered message to know when incoming
+ // mutation events should be applied. Our valuemap is plucked
+ // from ipc stream out of order, mutations in front if it need
+ // to be ignored.
+
+ // Ignore all mutations until OnLoadComplete time.
+ ignore_all_mutations_ = true;
+ ValuesMap values;
+ proxy_->LoadArea(
+ connection_id, &values,
+ base::Bind(&DomStorageCachedArea::OnLoadComplete,
+ weak_factory_.GetWeakPtr()));
+ map_ = new DomStorageMap(dom_storage::kPerAreaQuota);
+ map_->SwapValues(&values);
+}
+
+void DomStorageCachedArea::Reset() {
+ map_ = NULL;
+ weak_factory_.InvalidateWeakPtrs();
+ ignore_key_mutations_.clear();
+ ignore_all_mutations_ = false;
+}
+
+void DomStorageCachedArea::OnLoadComplete(bool success) {
+ DCHECK(success);
+ DCHECK(ignore_all_mutations_);
+ ignore_all_mutations_ = false;
+}
+
+void DomStorageCachedArea::OnSetItemComplete(
+ const string16& key, bool success) {
+ if (!success) {
+ Reset();
+ return;
+ }
+ std::map<string16, int>::iterator found = ignore_key_mutations_.find(key);
+ DCHECK(found != ignore_key_mutations_.end());
+ if (--found->second == 0)
+ ignore_key_mutations_.erase(found);
+}
+
+void DomStorageCachedArea::OnRemoveItemComplete(
+ const string16& key, bool success) {
+ DCHECK(success);
+ std::map<string16, int>::iterator found = ignore_key_mutations_.find(key);
+ DCHECK(found != ignore_key_mutations_.end());
+ if (--found->second == 0)
+ ignore_key_mutations_.erase(found);
+}
+
+void DomStorageCachedArea::OnClearComplete(bool success) {
+ DCHECK(success);
+ DCHECK(ignore_all_mutations_);
+ ignore_all_mutations_ = false;
+}
+
+} // namespace dom_storage
diff --git a/webkit/dom_storage/dom_storage_cached_area.h b/webkit/dom_storage/dom_storage_cached_area.h
new file mode 100644
index 0000000..1532afd
--- /dev/null
+++ b/webkit/dom_storage/dom_storage_cached_area.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2012 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 WEBKIT_DOM_STORAGE_DOM_STORAGE_CACHED_AREA_H_
+#define WEBKIT_DOM_STORAGE_DOM_STORAGE_CACHED_AREA_H_
+#pragma once
+
+#include <map>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/nullable_string16.h"
+#include "googleurl/src/gurl.h"
+
+namespace dom_storage {
+
+class DomStorageMap;
+class DomStorageProxy;
+
+// Unlike the other classes in the dom_storage library, this one is intended
+// for use in renderer processes. It maintains a complete cach of the an
+// origin's Map of key/value pairs for fast access. The cache is primed on
+// first access and changes are written to the backend thru the |proxy|.
+// Mutations originating in other processes are applied to the cache via
+// the ApplyMutation method.
+class DomStorageCachedArea : public base::RefCounted<DomStorageCachedArea> {
+ public:
+ DomStorageCachedArea(int64 namespace_id, const GURL& origin,
+ DomStorageProxy* proxy);
+
+ int64 namespace_id() const { return namespace_id_; }
+ const GURL& origin() const { return origin_; }
+
+ unsigned GetLength(int connection_id);
+ NullableString16 GetKey(int connection_id, unsigned index);
+ NullableString16 GetItem(int connection_id, const string16& key);
+ bool SetItem(int connection_id, const string16& key, const string16& value,
+ const GURL& page_url);
+ void RemoveItem(int connection_id, const string16& key,
+ const GURL& page_url);
+ void Clear(int connection_id, const GURL& page_url);
+
+ void ApplyMutation(const NullableString16& key,
+ const NullableString16& new_value);
+
+ private:
+ friend class DomStorageCachedAreaTest;
+ friend class base::RefCounted<DomStorageCachedArea>;
+ ~DomStorageCachedArea();
+
+ // Primes the cache, loading all values for the area.
+ void Prime(int connection_id);
+ void PrimeIfNeeded(int connection_id) {
+ if (!map_)
+ Prime(connection_id);
+ }
+
+ // Resets the object back to its newly constructed state.
+ void Reset();
+
+ // Async completion callbacks for proxied operations.
+ // These are used to maintain cache consistency by preventing
+ // mutation events from other processes from overwriting local
+ // changes made after the mutation.
+ void OnLoadComplete(bool success);
+ void OnSetItemComplete(const string16& key, bool success);
+ void OnClearComplete(bool success);
+ void OnRemoveItemComplete(const string16& key, bool success);
+
+ bool should_ignore_key_mutation(const string16& key) const {
+ return ignore_key_mutations_.find(key) != ignore_key_mutations_.end();
+ }
+
+ bool ignore_all_mutations_;
+ std::map<string16, int> ignore_key_mutations_;
+
+ int64 namespace_id_;
+ GURL origin_;
+ scoped_refptr<DomStorageMap> map_;
+ scoped_refptr<DomStorageProxy> proxy_;
+ base::WeakPtrFactory<DomStorageCachedArea> weak_factory_;
+};
+
+} // namespace dom_storage
+
+#endif // WEBKIT_DOM_STORAGE_DOM_STORAGE_CACHED_AREA_H_
diff --git a/webkit/dom_storage/dom_storage_cached_area_unittest.cc b/webkit/dom_storage/dom_storage_cached_area_unittest.cc
new file mode 100644
index 0000000..61b3cfe
--- /dev/null
+++ b/webkit/dom_storage/dom_storage_cached_area_unittest.cc
@@ -0,0 +1,349 @@
+// Copyright (c) 2012 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 <list>
+
+#include "base/bind.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/dom_storage/dom_storage_cached_area.h"
+#include "webkit/dom_storage/dom_storage_proxy.h"
+
+namespace dom_storage {
+
+namespace {
+// A mock implementation of the DomStorageProxy interface.
+class MockProxy : public DomStorageProxy {
+ public:
+ MockProxy() {
+ ResetObservations();
+ }
+
+ // DomStorageProxy interface for use by DomStorageCachedArea.
+
+ virtual void LoadArea(int connection_id, ValuesMap* values,
+ const CompletionCallback& callback) OVERRIDE {
+ pending_callbacks_.push_back(callback);
+ observed_load_area_ = true;
+ observed_connection_id_ = connection_id;
+ *values = load_area_return_values_;
+ }
+
+ virtual void SetItem(int connection_id, const string16& key,
+ const string16& value, const GURL& page_url,
+ const CompletionCallback& callback) OVERRIDE {
+ pending_callbacks_.push_back(callback);
+ observed_set_item_ = true;
+ observed_connection_id_ = connection_id;
+ observed_key_ = key;
+ observed_value_ = value;
+ observed_page_url_ = page_url;
+ }
+
+ virtual void RemoveItem(int connection_id, const string16& key,
+ const GURL& page_url,
+ const CompletionCallback& callback) OVERRIDE {
+ pending_callbacks_.push_back(callback);
+ observed_remove_item_ = true;
+ observed_connection_id_ = connection_id;
+ observed_key_ = key;
+ observed_page_url_ = page_url;
+ }
+
+ virtual void ClearArea(int connection_id,
+ const GURL& page_url,
+ const CompletionCallback& callback) OVERRIDE {
+ pending_callbacks_.push_back(callback);
+ observed_clear_area_ = true;
+ observed_connection_id_ = connection_id;
+ observed_page_url_ = page_url;
+ }
+
+ // Methods and members for use by test fixtures.
+
+ void ResetObservations() {
+ observed_load_area_ = false;
+ observed_set_item_ = false;
+ observed_remove_item_ = false;
+ observed_clear_area_ = false;
+ observed_connection_id_ = 0;
+ observed_key_.clear();
+ observed_value_.clear();
+ observed_page_url_ = GURL();
+ }
+
+ void CompleteAllPendingCallbacks() {
+ while (!pending_callbacks_.empty())
+ CompleteOnePendingCallback(true);
+ }
+
+ void CompleteOnePendingCallback(bool success) {
+ ASSERT_TRUE(!pending_callbacks_.empty());
+ pending_callbacks_.front().Run(success);
+ pending_callbacks_.pop_front();
+ }
+
+ typedef std::list<CompletionCallback> CallbackList;
+
+ ValuesMap load_area_return_values_;
+ CallbackList pending_callbacks_;
+ bool observed_load_area_;
+ bool observed_set_item_;
+ bool observed_remove_item_;
+ bool observed_clear_area_;
+ int observed_connection_id_;
+ string16 observed_key_;
+ string16 observed_value_;
+ GURL observed_page_url_;
+
+ private:
+ virtual ~MockProxy() {}
+};
+} // namespace
+
+class DomStorageCachedAreaTest : public testing::Test {
+ public:
+ DomStorageCachedAreaTest()
+ : kNamespaceId(10),
+ kOrigin("http://dom_storage/"),
+ kKey(ASCIIToUTF16("key")),
+ kValue(ASCIIToUTF16("value")),
+ kPageUrl("http://dom_storage/page") {
+ }
+
+ const int64 kNamespaceId;
+ const GURL kOrigin;
+ const string16 kKey;
+ const string16 kValue;
+ const GURL kPageUrl;
+
+ virtual void SetUp() {
+ mock_proxy_ = new MockProxy();
+ }
+
+ bool IsPrimed(DomStorageCachedArea* cached_area) {
+ return cached_area->map_.get();
+ }
+
+ bool IsIgnoringAllMutations(DomStorageCachedArea* cached_area) {
+ return cached_area->ignore_all_mutations_;
+ }
+
+ bool IsIgnoringKeyMutations(DomStorageCachedArea* cached_area,
+ const string16& key) {
+ return cached_area->should_ignore_key_mutation(key);
+ }
+
+ void ResetAll(DomStorageCachedArea* cached_area) {
+ cached_area->Reset();
+ mock_proxy_->ResetObservations();
+ mock_proxy_->pending_callbacks_.clear();
+ }
+
+ void ResetCacheOnly(DomStorageCachedArea* cached_area) {
+ cached_area->Reset();
+ }
+
+ protected:
+ scoped_refptr<MockProxy> mock_proxy_;
+};
+
+TEST_F(DomStorageCachedAreaTest, Basics) {
+ EXPECT_TRUE(mock_proxy_->HasOneRef());
+ scoped_refptr<DomStorageCachedArea> cached_area =
+ new DomStorageCachedArea(kNamespaceId, kOrigin, mock_proxy_);
+ EXPECT_EQ(kNamespaceId, cached_area->namespace_id());
+ EXPECT_EQ(kOrigin, cached_area->origin());
+ EXPECT_FALSE(mock_proxy_->HasOneRef());
+ cached_area->ApplyMutation(NullableString16(kKey, false),
+ NullableString16(kValue, false));
+ EXPECT_FALSE(IsPrimed(cached_area));
+
+ ResetAll(cached_area);
+ EXPECT_EQ(kNamespaceId, cached_area->namespace_id());
+ EXPECT_EQ(kOrigin, cached_area->origin());
+
+ const int kConnectionId = 1;
+ EXPECT_EQ(0u, cached_area->GetLength(kConnectionId));
+ EXPECT_TRUE(cached_area->SetItem(kConnectionId, kKey, kValue, kPageUrl));
+ EXPECT_EQ(1u, cached_area->GetLength(kConnectionId));
+ EXPECT_EQ(kKey, cached_area->GetKey(kConnectionId, 0).string());
+ EXPECT_EQ(kValue, cached_area->GetItem(kConnectionId, kKey).string());
+ cached_area->RemoveItem(kConnectionId, kKey, kPageUrl);
+ EXPECT_EQ(0u, cached_area->GetLength(kConnectionId));
+}
+
+TEST_F(DomStorageCachedAreaTest, Getters) {
+ const int kConnectionId = 7;
+ scoped_refptr<DomStorageCachedArea> cached_area =
+ new DomStorageCachedArea(kNamespaceId, kOrigin, mock_proxy_);
+
+ // GetLength, we expect to see one call to load in the proxy.
+ EXPECT_FALSE(IsPrimed(cached_area));
+ EXPECT_EQ(0u, cached_area->GetLength(kConnectionId));
+ EXPECT_TRUE(IsPrimed(cached_area));
+ EXPECT_TRUE(mock_proxy_->observed_load_area_);
+ EXPECT_EQ(kConnectionId, mock_proxy_->observed_connection_id_);
+ EXPECT_EQ(1u, mock_proxy_->pending_callbacks_.size());
+ EXPECT_TRUE(IsIgnoringAllMutations(cached_area));
+ mock_proxy_->CompleteAllPendingCallbacks();
+ EXPECT_FALSE(IsIgnoringAllMutations(cached_area));
+
+ // GetKey, expect the one call to load.
+ ResetAll(cached_area);
+ EXPECT_FALSE(IsPrimed(cached_area));
+ EXPECT_TRUE(cached_area->GetKey(kConnectionId, 2).is_null());
+ EXPECT_TRUE(IsPrimed(cached_area));
+ EXPECT_TRUE(mock_proxy_->observed_load_area_);
+ EXPECT_EQ(kConnectionId, mock_proxy_->observed_connection_id_);
+ EXPECT_EQ(1u, mock_proxy_->pending_callbacks_.size());
+
+ // GetItem, ditto.
+ ResetAll(cached_area);
+ EXPECT_FALSE(IsPrimed(cached_area));
+ EXPECT_TRUE(cached_area->GetItem(kConnectionId, kKey).is_null());
+ EXPECT_TRUE(IsPrimed(cached_area));
+ EXPECT_TRUE(mock_proxy_->observed_load_area_);
+ EXPECT_EQ(kConnectionId, mock_proxy_->observed_connection_id_);
+ EXPECT_EQ(1u, mock_proxy_->pending_callbacks_.size());
+}
+
+TEST_F(DomStorageCachedAreaTest, Setters) {
+ const int kConnectionId = 7;
+ scoped_refptr<DomStorageCachedArea> cached_area =
+ new DomStorageCachedArea(kNamespaceId, kOrigin, mock_proxy_);
+
+ // SetItem, we expect a call to load followed by a call to set item
+ // in the proxy.
+ EXPECT_FALSE(IsPrimed(cached_area));
+ EXPECT_TRUE(cached_area->SetItem(kConnectionId, kKey, kValue, kPageUrl));
+ EXPECT_TRUE(IsPrimed(cached_area));
+ EXPECT_TRUE(mock_proxy_->observed_load_area_);
+ EXPECT_TRUE(mock_proxy_->observed_set_item_);
+ EXPECT_EQ(kConnectionId, mock_proxy_->observed_connection_id_);
+ EXPECT_EQ(kPageUrl, mock_proxy_->observed_page_url_);
+ EXPECT_EQ(kKey, mock_proxy_->observed_key_);
+ EXPECT_EQ(kValue, mock_proxy_->observed_value_);
+ EXPECT_EQ(2u, mock_proxy_->pending_callbacks_.size());
+
+ // Clear, we expect a just the one call to clear in the proxy since
+ // there's no need to load the data prior to deleting it.
+ ResetAll(cached_area);
+ EXPECT_FALSE(IsPrimed(cached_area));
+ cached_area->Clear(kConnectionId, kPageUrl);
+ EXPECT_TRUE(IsPrimed(cached_area));
+ EXPECT_TRUE(mock_proxy_->observed_clear_area_);
+ EXPECT_EQ(kConnectionId, mock_proxy_->observed_connection_id_);
+ EXPECT_EQ(kPageUrl, mock_proxy_->observed_page_url_);
+ EXPECT_EQ(1u, mock_proxy_->pending_callbacks_.size());
+
+ // RemoveItem with nothing to remove, expect just one call to load.
+ ResetAll(cached_area);
+ EXPECT_FALSE(IsPrimed(cached_area));
+ cached_area->RemoveItem(kConnectionId, kKey, kPageUrl);
+ EXPECT_TRUE(IsPrimed(cached_area));
+ EXPECT_TRUE(mock_proxy_->observed_load_area_);
+ EXPECT_FALSE(mock_proxy_->observed_remove_item_);
+ EXPECT_EQ(kConnectionId, mock_proxy_->observed_connection_id_);
+ EXPECT_EQ(1u, mock_proxy_->pending_callbacks_.size());
+
+ // RemoveItem with something to remove, expect a call to load followed
+ // by a call to remove.
+ ResetAll(cached_area);
+ mock_proxy_->load_area_return_values_[kKey] = NullableString16(kValue, false);
+ EXPECT_FALSE(IsPrimed(cached_area));
+ cached_area->RemoveItem(kConnectionId, kKey, kPageUrl);
+ EXPECT_TRUE(IsPrimed(cached_area));
+ EXPECT_TRUE(mock_proxy_->observed_load_area_);
+ EXPECT_TRUE(mock_proxy_->observed_remove_item_);
+ EXPECT_EQ(kConnectionId, mock_proxy_->observed_connection_id_);
+ EXPECT_EQ(kPageUrl, mock_proxy_->observed_page_url_);
+ EXPECT_EQ(kKey, mock_proxy_->observed_key_);
+ EXPECT_EQ(2u, mock_proxy_->pending_callbacks_.size());
+}
+
+TEST_F(DomStorageCachedAreaTest, MutationsAreIgnoredUntilLoadCompletion) {
+ const int kConnectionId = 7;
+ scoped_refptr<DomStorageCachedArea> cached_area =
+ new DomStorageCachedArea(kNamespaceId, kOrigin, mock_proxy_);
+ EXPECT_TRUE(cached_area->GetItem(kConnectionId, kKey).is_null());
+ EXPECT_TRUE(IsPrimed(cached_area));
+ EXPECT_TRUE(IsIgnoringAllMutations(cached_area));
+
+ // Before load completion, the mutation should be ignored.
+ cached_area->ApplyMutation(NullableString16(kKey, false),
+ NullableString16(kValue, false));
+ EXPECT_TRUE(cached_area->GetItem(kConnectionId, kKey).is_null());
+
+ // Call the load completion callback.
+ mock_proxy_->CompleteOnePendingCallback(true);
+ EXPECT_FALSE(IsIgnoringAllMutations(cached_area));
+
+ // Verify that mutations are now applied.
+ cached_area->ApplyMutation(NullableString16(kKey, false),
+ NullableString16(kValue, false));
+ EXPECT_EQ(kValue, cached_area->GetItem(kConnectionId, kKey).string());
+}
+
+TEST_F(DomStorageCachedAreaTest, MutationsAreIgnoredUntilClearCompletion) {
+ const int kConnectionId = 4;
+ scoped_refptr<DomStorageCachedArea> cached_area =
+ new DomStorageCachedArea(kNamespaceId, kOrigin, mock_proxy_);
+ cached_area->Clear(kConnectionId, kPageUrl);
+ EXPECT_TRUE(IsIgnoringAllMutations(cached_area));
+ mock_proxy_->CompleteOnePendingCallback(true);
+ EXPECT_FALSE(IsIgnoringAllMutations(cached_area));
+
+ // Verify that calling Clear twice works as expected, the first
+ // completion callback should have been cancelled.
+ ResetCacheOnly(cached_area);
+ cached_area->Clear(kConnectionId, kPageUrl);
+ EXPECT_TRUE(IsIgnoringAllMutations(cached_area));
+ cached_area->Clear(kConnectionId, kPageUrl);
+ EXPECT_TRUE(IsIgnoringAllMutations(cached_area));
+ mock_proxy_->CompleteOnePendingCallback(true);
+ EXPECT_TRUE(IsIgnoringAllMutations(cached_area));
+ mock_proxy_->CompleteOnePendingCallback(true);
+ EXPECT_FALSE(IsIgnoringAllMutations(cached_area));
+}
+
+TEST_F(DomStorageCachedAreaTest, KeyMutationsAreIgnoredUntilCompletion) {
+ const int kConnectionId = 8;
+ scoped_refptr<DomStorageCachedArea> cached_area =
+ new DomStorageCachedArea(kNamespaceId, kOrigin, mock_proxy_);
+
+ // SetItem
+ EXPECT_TRUE(cached_area->SetItem(kConnectionId, kKey, kValue, kPageUrl));
+ mock_proxy_->CompleteOnePendingCallback(true); // load completion
+ EXPECT_FALSE(IsIgnoringAllMutations(cached_area));
+ EXPECT_TRUE(IsIgnoringKeyMutations(cached_area, kKey));
+ cached_area->ApplyMutation(NullableString16(kKey, false),
+ NullableString16(true));
+ EXPECT_EQ(kValue, cached_area->GetItem(kConnectionId, kKey).string());
+ mock_proxy_->CompleteOnePendingCallback(true); // set completion
+ EXPECT_FALSE(IsIgnoringKeyMutations(cached_area, kKey));
+
+ // RemoveItem
+ cached_area->RemoveItem(kConnectionId, kKey, kPageUrl);
+ EXPECT_TRUE(IsIgnoringKeyMutations(cached_area, kKey));
+ mock_proxy_->CompleteOnePendingCallback(true); // remove completion
+ EXPECT_FALSE(IsIgnoringKeyMutations(cached_area, kKey));
+
+ // Multiple mutations to the same key.
+ EXPECT_TRUE(cached_area->SetItem(kConnectionId, kKey, kValue, kPageUrl));
+ cached_area->RemoveItem(kConnectionId, kKey, kPageUrl);
+ EXPECT_TRUE(IsIgnoringKeyMutations(cached_area, kKey));
+ mock_proxy_->CompleteOnePendingCallback(true); // set completion
+ EXPECT_TRUE(IsIgnoringKeyMutations(cached_area, kKey));
+ mock_proxy_->CompleteOnePendingCallback(true); // remove completion
+ EXPECT_FALSE(IsIgnoringKeyMutations(cached_area, kKey));
+
+ // A failed set item operation should Reset the cache.
+ EXPECT_TRUE(cached_area->SetItem(kConnectionId, kKey, kValue, kPageUrl));
+ EXPECT_TRUE(IsIgnoringKeyMutations(cached_area, kKey));
+ mock_proxy_->CompleteOnePendingCallback(false);
+ EXPECT_FALSE(IsPrimed(cached_area));
+}
+
+} // namespace dom_storage
diff --git a/webkit/dom_storage/dom_storage_map.h b/webkit/dom_storage/dom_storage_map.h
index 5a4b682..108716d 100644
--- a/webkit/dom_storage/dom_storage_map.h
+++ b/webkit/dom_storage/dom_storage_map.h
@@ -21,7 +21,7 @@ namespace dom_storage {
class DomStorageMap
: public base::RefCountedThreadSafe<DomStorageMap> {
public:
- DomStorageMap(size_t quota);
+ explicit DomStorageMap(size_t quota);
unsigned Length() const;
NullableString16 Key(unsigned index);
@@ -43,6 +43,8 @@ class DomStorageMap
DomStorageMap* DeepCopy() const;
size_t bytes_used() const { return bytes_used_; }
+ size_t quota() const { return quota_; }
+ void set_quota(size_t quota) { quota_ = quota; }
private:
friend class base::RefCountedThreadSafe<DomStorageMap>;
diff --git a/webkit/dom_storage/dom_storage_proxy.h b/webkit/dom_storage/dom_storage_proxy.h
new file mode 100644
index 0000000..81cae66
--- /dev/null
+++ b/webkit/dom_storage/dom_storage_proxy.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 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 WEBKIT_DOM_STORAGE_CACHED_AREA_H_
+#define WEBKIT_DOM_STORAGE_CACHED_AREA_H_
+#pragma once
+
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/nullable_string16.h"
+#include "base/string16.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/dom_storage/dom_storage_types.h"
+
+namespace dom_storage {
+
+// Abstract interface for cached area, renderer to browser communications.
+class DomStorageProxy : public base::RefCounted<DomStorageProxy> {
+ public:
+ typedef base::Callback<void(bool)> CompletionCallback;
+
+ virtual void LoadArea(int connection_id, ValuesMap* values,
+ const CompletionCallback& callback) = 0;
+
+ virtual void SetItem(int connection_id, const string16& key,
+ const string16& value, const GURL& page_url,
+ const CompletionCallback& callback) = 0;
+
+ virtual void RemoveItem(int connection_id, const string16& key,
+ const GURL& page_url,
+ const CompletionCallback& callback) = 0;
+
+ virtual void ClearArea(int connection_id,
+ const GURL& page_url,
+ const CompletionCallback& callback) = 0;
+ protected:
+ friend class base::RefCounted<DomStorageProxy>;
+ virtual ~DomStorageProxy() {}
+};
+
+} // namespace dom_storage
+
+#endif // WEBKIT_DOM_STORAGE_DOM_STORAGE_AREA_H_
diff --git a/webkit/dom_storage/webkit_dom_storage.gypi b/webkit/dom_storage/webkit_dom_storage.gypi
index 82f6670..525617d 100644
--- a/webkit/dom_storage/webkit_dom_storage.gypi
+++ b/webkit/dom_storage/webkit_dom_storage.gypi
@@ -18,6 +18,8 @@
'sources': [
'dom_storage_area.cc',
'dom_storage_area.h',
+ 'dom_storage_cached_area.cc',
+ 'dom_storage_cached_area.h',
'dom_storage_context.cc',
'dom_storage_context.h',
'dom_storage_database.cc',
@@ -28,6 +30,7 @@
'dom_storage_map.h',
'dom_storage_namespace.cc',
'dom_storage_namespace.h',
+ 'dom_storage_proxy.h',
'dom_storage_session.cc',
'dom_storage_session.h',
'dom_storage_task_runner.cc',