diff options
author | michaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-24 22:56:50 +0000 |
---|---|---|
committer | michaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-24 22:56:50 +0000 |
commit | bc4297aad8940a1d13d92f7d71c0ee6d1c8c7aaa (patch) | |
tree | 59af1a81ebc62f24c7b458c92fbd1c99aee496c2 /webkit/dom_storage | |
parent | 7a8d1ed4bc596d42a468f7c41776b4f433c4e5a0 (diff) | |
download | chromium_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.cc | 194 | ||||
-rw-r--r-- | webkit/dom_storage/dom_storage_cached_area.h | 87 | ||||
-rw-r--r-- | webkit/dom_storage/dom_storage_cached_area_unittest.cc | 349 | ||||
-rw-r--r-- | webkit/dom_storage/dom_storage_map.h | 4 | ||||
-rw-r--r-- | webkit/dom_storage/dom_storage_proxy.h | 44 | ||||
-rw-r--r-- | webkit/dom_storage/webkit_dom_storage.gypi | 3 |
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', |