From 7337bd98d54955a98a1e339367ea21d2c98e49cc Mon Sep 17 00:00:00 2001 From: michaeln Date: Wed, 6 Apr 2016 13:03:14 -0700 Subject: [PATCH] Some fleshing out the mojo based localstorage implementation. Data is not yet stored on disk, but its functional from a pages point of view. The mojo based plumbing between the caching layers in the renderer and and browser process is working. The browser side now enforces a size constraint. The observer on the renderer side now longer lived so storage events are not dropped. Events have to be raised even if the local cache is not loaded. BUG=586194 Review URL: https://codereview.chromium.org/1837883003 Cr-Commit-Position: refs/heads/master@{#385533} --- .../dom_storage_context_wrapper.cc | 24 +++- .../dom_storage/dom_storage_context_wrapper.h | 1 + content/browser/leveldb_wrapper_impl.cc | 105 +++++++++++--- content/browser/leveldb_wrapper_impl.h | 18 ++- content/browser/storage_partition_impl.cc | 4 +- content/browser/storage_partition_impl.h | 1 + content/common/leveldb_wrapper.mojom | 24 ++-- .../common/storage_partition_service.mojom | 4 +- .../dom_storage/local_storage_cached_area.cc | 132 +++++++++++------- .../dom_storage/local_storage_cached_area.h | 21 ++- mojo/public/cpp/bindings/array.h | 10 ++ 11 files changed, 247 insertions(+), 97 deletions(-) diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.cc b/content/browser/dom_storage/dom_storage_context_wrapper.cc index 0490d2e0b1342d..b0ec4d5ab6545f 100644 --- a/content/browser/dom_storage/dom_storage_context_wrapper.cc +++ b/content/browser/dom_storage/dom_storage_context_wrapper.cc @@ -84,10 +84,11 @@ class DOMStorageContextWrapper::MojoState { weak_ptr_factory_(this) {} void OpenLocalStorage(const url::Origin& origin, + mojom::LevelDBObserverPtr observer, mojom::LevelDBWrapperRequest request); private: - void LevelDBWrapperImplHasNoBindings(const url::Origin& origin) { + void OnLevelDDWrapperHasNoBindings(const url::Origin& origin) { DCHECK(level_db_wrappers_.find(origin) != level_db_wrappers_.end()); level_db_wrappers_.erase(origin); } @@ -99,6 +100,7 @@ class DOMStorageContextWrapper::MojoState { // The (possibly delayed) implementation of OpenLocalStorage(). Can be called // directly from that function, or through |on_database_open_callbacks_|. void BindLocalStorage(const url::Origin& origin, + mojom::LevelDBObserverPtr observer, mojom::LevelDBWrapperRequest request); // Maps between an origin and its prefixed LevelDB view. @@ -127,6 +129,7 @@ class DOMStorageContextWrapper::MojoState { void DOMStorageContextWrapper::MojoState::OpenLocalStorage( const url::Origin& origin, + mojom::LevelDBObserverPtr observer, mojom::LevelDBWrapperRequest request) { // If we don't have a filesystem_connection_, we'll need to establish one. if (connection_state_ == NO_CONNECTION) { @@ -158,11 +161,11 @@ void DOMStorageContextWrapper::MojoState::OpenLocalStorage( // Queue this OpenLocalStorage call for when we have a level db pointer. on_database_opened_callbacks_.push_back( base::Bind(&MojoState::BindLocalStorage, weak_ptr_factory_.GetWeakPtr(), - origin, base::Passed(&request))); + origin, base::Passed(&observer), base::Passed(&request))); return; } - BindLocalStorage(origin, std::move(request)); + BindLocalStorage(origin, std::move(observer), std::move(request)); } void DOMStorageContextWrapper::MojoState::OnDirectoryOpened( @@ -208,17 +211,22 @@ void DOMStorageContextWrapper::MojoState::OnDatabaseOpened( void DOMStorageContextWrapper::MojoState::BindLocalStorage( const url::Origin& origin, + mojom::LevelDBObserverPtr observer, mojom::LevelDBWrapperRequest request) { - if (level_db_wrappers_.find(origin) == level_db_wrappers_.end()) { + auto found = level_db_wrappers_.find(origin); + if (found == level_db_wrappers_.end()) { level_db_wrappers_[origin] = make_scoped_ptr(new LevelDBWrapperImpl( database_.get(), origin.Serialize(), - base::Bind(&MojoState::LevelDBWrapperImplHasNoBindings, + kPerStorageAreaQuota + kPerStorageAreaOverQuotaAllowance, + base::Bind(&MojoState::OnLevelDDWrapperHasNoBindings, base::Unretained(this), origin))); + found = level_db_wrappers_.find(origin); } - level_db_wrappers_[origin]->Bind(std::move(request)); + found->second->Bind(std::move(request)); + found->second->AddObserver(std::move(observer)); } DOMStorageContextWrapper::DOMStorageContextWrapper( @@ -336,8 +344,10 @@ void DOMStorageContextWrapper::Flush() { void DOMStorageContextWrapper::OpenLocalStorage( const url::Origin& origin, + mojom::LevelDBObserverPtr observer, mojom::LevelDBWrapperRequest request) { - mojo_state_->OpenLocalStorage(origin, std::move(request)); + mojo_state_->OpenLocalStorage( + origin, std::move(observer), std::move(request)); } } // namespace content diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.h b/content/browser/dom_storage/dom_storage_context_wrapper.h index 83b811dcb41522..56196a1f997f11 100644 --- a/content/browser/dom_storage/dom_storage_context_wrapper.h +++ b/content/browser/dom_storage/dom_storage_context_wrapper.h @@ -66,6 +66,7 @@ class CONTENT_EXPORT DOMStorageContextWrapper : // See mojom::StoragePartitionService interface. void OpenLocalStorage(const url::Origin& origin, + mojom::LevelDBObserverPtr observer, mojom::LevelDBWrapperRequest request); private: diff --git a/content/browser/leveldb_wrapper_impl.cc b/content/browser/leveldb_wrapper_impl.cc index ed58624dfd412e..0d3938915f87ad 100644 --- a/content/browser/leveldb_wrapper_impl.cc +++ b/content/browser/leveldb_wrapper_impl.cc @@ -5,16 +5,20 @@ #include "content/browser/leveldb_wrapper_impl.h" #include "base/bind.h" +#include "mojo/common/common_type_converters.h" namespace content { LevelDBWrapperImpl::LevelDBWrapperImpl( leveldb::LevelDBDatabase* database, const std::string& prefix, + size_t max_size, const base::Closure& no_bindings_callback) : prefix_(prefix), no_bindings_callback_(no_bindings_callback), - database_(database) { + database_(database), + bytes_used_(0), + max_size_(max_size) { bindings_.set_connection_error_handler(base::Bind( &LevelDBWrapperImpl::OnConnectionError, base::Unretained(this))); } @@ -23,43 +27,110 @@ void LevelDBWrapperImpl::Bind(mojom::LevelDBWrapperRequest request) { bindings_.AddBinding(this, std::move(request)); } +void LevelDBWrapperImpl::AddObserver(mojom::LevelDBObserverPtr observer) { + observers_.AddInterfacePtr(std::move(observer)); +} + LevelDBWrapperImpl::~LevelDBWrapperImpl() {} void LevelDBWrapperImpl::Put(mojo::Array key, mojo::Array value, const mojo::String& source, const PutCallback& callback) { - NOTIMPLEMENTED(); - callback.Run(leveldb::DatabaseError::NOT_SUPPORTED); + bool has_old_item = false; + mojo::Array old_value; + size_t old_item_size = 0; + auto found = map_.find(key); + if (found != map_.end()) { + old_value = std::move(found->second); + old_item_size = key.size() + old_value.size(); + has_old_item = true; + } + size_t new_item_size = key.size() + value.size(); + size_t new_bytes_used = bytes_used_ - old_item_size + new_item_size; + + // Only check quota if the size is increasing, this allows + // shrinking changes to pre-existing maps that are over budget. + if (new_item_size > old_item_size && new_bytes_used > max_size_) { + callback.Run(false); + return; + } + + map_[key.Clone()] = value.Clone(); + bytes_used_ = new_bytes_used; + if (!has_old_item) { + // We added a new key/value pair. + observers_.ForAllPtrs( + [&key, &value, &source](mojom::LevelDBObserver* observer) { + observer->KeyAdded(key.Clone(), value.Clone(), source); + }); + } else { + // We changed the value for an existing key. + observers_.ForAllPtrs( + [&key, &value, &source, &old_value](mojom::LevelDBObserver* observer) { + observer->KeyChanged( + key.Clone(), value.Clone(), old_value.Clone(), source); + }); + } + callback.Run(true); } void LevelDBWrapperImpl::Delete(mojo::Array key, const mojo::String& source, const DeleteCallback& callback) { - NOTIMPLEMENTED(); - callback.Run(leveldb::DatabaseError::NOT_SUPPORTED); + auto found = map_.find(key); + if (found == map_.end()) { + callback.Run(true); + return; + } + + mojo::Array old_value = std::move(found->second); + map_.erase(found); + bytes_used_ -= key.size() + old_value.size(); + observers_.ForAllPtrs( + [&key, &source, &old_value](mojom::LevelDBObserver* observer) { + observer->KeyDeleted( + key.Clone(), old_value.Clone(), source); + }); + callback.Run(true); } -void LevelDBWrapperImpl::DeleteAll(mojom::LevelDBObserverPtr observer, - const mojo::String& source, +void LevelDBWrapperImpl::DeleteAll(const mojo::String& source, const DeleteAllCallback& callback) { - // TODO(jam): store observer and call it when changes occur. - NOTIMPLEMENTED(); - callback.Run(leveldb::DatabaseError::NOT_SUPPORTED); + map_.clear(); + bytes_used_ = 0; + observers_.ForAllPtrs( + [&source](mojom::LevelDBObserver* observer) { + observer->AllDeleted(source); + }); + callback.Run(true); } void LevelDBWrapperImpl::Get(mojo::Array key, const GetCallback& callback) { - NOTIMPLEMENTED(); - callback.Run(leveldb::DatabaseError::NOT_SUPPORTED, mojo::Array()); + auto found = map_.find(key); + if (found == map_.end()) { + callback.Run(false, mojo::Array()); + return; + } + callback.Run(true, found->second.Clone()); } -void LevelDBWrapperImpl::GetAll(mojom::LevelDBObserverPtr observer, +void LevelDBWrapperImpl::GetAll(const mojo::String& source, const GetAllCallback& callback) { - // TODO(jam): store observer and call it when changes occur. - NOTIMPLEMENTED(); - callback.Run(leveldb::DatabaseError::NOT_SUPPORTED, - mojo::Array()); + + mojo::Array all(map_.size()); + for (const auto& it : map_) { + mojom::KeyValuePtr kv = mojom::KeyValue::New(); + kv->key = it.first.Clone(); + kv->value = it.second.Clone(); + all.push_back(std::move(kv)); + } + callback.Run(leveldb::DatabaseError::OK, std::move(all)); + observers_.ForAllPtrs( + [source](mojom::LevelDBObserver* observer) { + observer->GetAllComplete(source); + }); } void LevelDBWrapperImpl::OnConnectionError() { diff --git a/content/browser/leveldb_wrapper_impl.h b/content/browser/leveldb_wrapper_impl.h index 9c4434baffbd6c..c96edf9ca6bca9 100644 --- a/content/browser/leveldb_wrapper_impl.h +++ b/content/browser/leveldb_wrapper_impl.h @@ -9,6 +9,7 @@ #include "base/macros.h" #include "content/common/leveldb_wrapper.mojom.h" #include "mojo/public/cpp/bindings/binding_set.h" +#include "mojo/public/cpp/bindings/interface_ptr_set.h" namespace content { @@ -17,19 +18,21 @@ namespace content { // 1) Adds the given prefix, if any, to all keys. This allows the sharing of one // database across many, possibly untrusted, consumers and ensuring that they // can't access each other's values. -// 2) Informs an observer when the given prefix' values are modified by another -// process. -// 3) Throttles requests to avoid overwhelming the disk. +// 2) Enforces a max_size constraint. +// 3) Informs an observer when the given prefix' values are modified. +// 4) Throttles requests to avoid overwhelming the disk. class LevelDBWrapperImpl : public mojom::LevelDBWrapper { public: // |no_bindings_callback| will be called when this object has no more // bindings. LevelDBWrapperImpl(leveldb::LevelDBDatabase* database, const std::string& prefix, + size_t max_size, const base::Closure& no_bindings_callback); ~LevelDBWrapperImpl() override; void Bind(mojom::LevelDBWrapperRequest request); + void AddObserver(mojom::LevelDBObserverPtr observer); private: // LevelDBWrapperImpl: @@ -40,19 +43,22 @@ class LevelDBWrapperImpl : public mojom::LevelDBWrapper { void Delete(mojo::Array key, const mojo::String& source, const DeleteCallback& callback) override; - void DeleteAll(mojom::LevelDBObserverPtr observer, - const mojo::String& source, + void DeleteAll(const mojo::String& source, const DeleteAllCallback& callback) override; void Get(mojo::Array key, const GetCallback& callback) override; - void GetAll(mojom::LevelDBObserverPtr observer, + void GetAll(const mojo::String& source, const GetAllCallback& callback) override; void OnConnectionError(); std::string prefix_; mojo::BindingSet bindings_; + mojo::InterfacePtrSet observers_; base::Closure no_bindings_callback_; leveldb::LevelDBDatabase* database_; + std::map, mojo::Array> map_; + size_t bytes_used_; + size_t max_size_; DISALLOW_COPY_AND_ASSIGN(LevelDBWrapperImpl); }; diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc index 32f552ed7a5471..28b0eb13450342 100644 --- a/content/browser/storage_partition_impl.cc +++ b/content/browser/storage_partition_impl.cc @@ -587,8 +587,10 @@ BackgroundSyncContextImpl* StoragePartitionImpl::GetBackgroundSyncContext() { void StoragePartitionImpl::OpenLocalStorage( const url::Origin& origin, + mojom::LevelDBObserverPtr observer, mojo::InterfaceRequest request) { - dom_storage_context_->OpenLocalStorage(origin, std::move(request)); + dom_storage_context_->OpenLocalStorage( + origin, std::move(observer), std::move(request)); } void StoragePartitionImpl::ClearDataImpl( diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h index 45efae4622c962..d564f9ec235a29 100644 --- a/content/browser/storage_partition_impl.h +++ b/content/browser/storage_partition_impl.h @@ -65,6 +65,7 @@ class StoragePartitionImpl : public StoragePartition, // mojom::StoragePartitionService interface. void OpenLocalStorage( const url::Origin& origin, + mojom::LevelDBObserverPtr observer, mojo::InterfaceRequest request) override; void ClearDataForOrigin(uint32_t remove_mask, diff --git a/content/common/leveldb_wrapper.mojom b/content/common/leveldb_wrapper.mojom index b3949ca855d57c..0035ebc5558fa9 100644 --- a/content/common/leveldb_wrapper.mojom +++ b/content/common/leveldb_wrapper.mojom @@ -7,19 +7,19 @@ module content.mojom; import "components/leveldb/public/interfaces/leveldb.mojom"; // Gives information about changes to a LevelDB database. -// The reason this is a parameter to DeleteAll and GetAll below, instead of -// being specified when opening a LevelDBWrapper, is to avoid the client getting -// callbacks for changes that have already been applied to its database that -// it's fetching via GetAll or it's clearing via DeleteAll. -// In the methods below, |source| is a user-defined string which was passed to -// the various LevelDBWrapper methods. // Note that observer methods are called before the callbacks for the // LevelDBWrapper methods are run. interface LevelDBObserver { + KeyAdded(array key, array value, string source); KeyChanged(array key, array new_value, array old_value, string source); KeyDeleted(array key, array old_value, string source); AllDeleted(string source); + + // Since the GetAll call is synchronous, observers need this asynchronously + // delivered notification to avoid applying changes to the returned array + // that it already contains. + GetAllComplete(string source); }; struct KeyValue { @@ -31,23 +31,21 @@ struct KeyValue { // change. interface LevelDBWrapper { // Sets the database entry for |key| to |value|. Returns OK on success. - Put(array key, array value, string source) - => (leveldb.DatabaseError status); + Put(array key, array value, string source) => (bool success); // Remove the database entry (if any) for |key|. Returns OK on success, and a // non-OK status on error. It is not an error if |key| did not exist in the // database. - Delete(array key, string source) => (leveldb.DatabaseError status); + Delete(array key, string source) => (bool success); // Removes all the entries. - DeleteAll(LevelDBObserver observer, string source) - => (leveldb.DatabaseError status); + DeleteAll(string source) => (bool success); // Returns the value of the |key|. - Get(array key) => (leveldb.DatabaseError status, array value); + Get(array key) => (bool success, array value); // Only used with small databases. Returns all key/value pairs. [Sync] - GetAll(LevelDBObserver observer) + GetAll(string source) => (leveldb.DatabaseError status, array data); }; diff --git a/content/common/storage_partition_service.mojom b/content/common/storage_partition_service.mojom index e4048a5b3d4e7f..3338658618d4b8 100644 --- a/content/common/storage_partition_service.mojom +++ b/content/common/storage_partition_service.mojom @@ -9,5 +9,7 @@ import "url/mojo/origin.mojom"; // Returns services related to the current storage partition. interface StoragePartitionService { - OpenLocalStorage(url.mojom.Origin origin, LevelDBWrapper& database); + OpenLocalStorage(url.mojom.Origin origin, + LevelDBObserver observer, + LevelDBWrapper& database); }; diff --git a/content/renderer/dom_storage/local_storage_cached_area.cc b/content/renderer/dom_storage/local_storage_cached_area.cc index cb975a1d9ec302..1f191a20da2ddd 100644 --- a/content/renderer/dom_storage/local_storage_cached_area.cc +++ b/content/renderer/dom_storage/local_storage_cached_area.cc @@ -6,6 +6,8 @@ #include "base/bind.h" #include "base/metrics/histogram_macros.h" +#include "base/rand_util.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/time/time.h" #include "content/common/dom_storage/dom_storage_map.h" @@ -41,9 +43,10 @@ LocalStorageCachedArea::LocalStorageCachedArea( const url::Origin& origin, mojom::StoragePartitionService* storage_partition_service, LocalStorageCachedAreas* cached_areas) - : origin_(origin), binding_(this), cached_areas_(cached_areas) { + : origin_(origin), binding_(this), + cached_areas_(cached_areas), weak_factory_(this) { storage_partition_service->OpenLocalStorage( - origin_, mojo::GetProxy(&leveldb_)); + origin_, binding_.CreateInterfacePtrAndBind(), mojo::GetProxy(&leveldb_)); } LocalStorageCachedArea::~LocalStorageCachedArea() { @@ -86,7 +89,7 @@ bool LocalStorageCachedArea::SetItem(const base::string16& key, mojo::Array::From(value), PackSource(page_url, storage_area_id), base::Bind(&LocalStorageCachedArea::OnSetItemComplete, - base::Unretained(this), key)); + weak_factory_.GetWeakPtr(), key)); return true; } @@ -103,7 +106,7 @@ void LocalStorageCachedArea::RemoveItem(const base::string16& key, leveldb_->Delete(mojo::Array::From(key), PackSource(page_url, storage_area_id), base::Bind(&LocalStorageCachedArea::OnRemoveItemComplete, - base::Unretained(this), key)); + weak_factory_.GetWeakPtr(), key)); } void LocalStorageCachedArea::Clear(const GURL& page_url, @@ -112,11 +115,10 @@ void LocalStorageCachedArea::Clear(const GURL& page_url, Reset(); map_ = new DOMStorageMap(kPerStorageAreaQuota); - - leveldb_->DeleteAll(binding_.CreateInterfacePtrAndBind(), - PackSource(page_url, storage_area_id), + ignore_all_mutations_ = true; + leveldb_->DeleteAll(PackSource(page_url, storage_area_id), base::Bind(&LocalStorageCachedArea::OnClearComplete, - base::Unretained(this))); + weak_factory_.GetWeakPtr())); } void LocalStorageCachedArea::AreaCreated(LocalStorageArea* area) { @@ -127,39 +129,21 @@ void LocalStorageCachedArea::AreaDestroyed(LocalStorageArea* area) { areas_.erase(area->id()); } +void LocalStorageCachedArea::KeyAdded(mojo::Array key, + mojo::Array value, + const mojo::String& source) { + base::NullableString16 null_value; + KeyAddedOrChanged(std::move(key), std::move(value), + null_value, source); +} + void LocalStorageCachedArea::KeyChanged(mojo::Array key, mojo::Array new_value, mojo::Array old_value, const mojo::String& source) { - GURL page_url; - std::string storage_area_id; - UnpackSource(source, &page_url, &storage_area_id); - - base::string16 key_string = key.To(); - base::string16 new_value_string = new_value.To(); - - blink::WebStorageArea* originating_area = nullptr; - if (areas_.find(storage_area_id) != areas_.end()) { - // The source storage area is in this process. - originating_area = areas_[storage_area_id]; - } else { - // This was from another process or the storage area is gone. If the former, - // apply it to our cache if we haven't already changed it and are waiting - // for the confirmation callback. In the latter case, we won't do anything - // because ignore_key_mutations_ won't be updated until the callback runs. - if (ignore_key_mutations_.find(key_string) != ignore_key_mutations_.end()) { - // We turn off quota checking here to accomodate the over budget allowance - // that's provided in the browser process. - base::NullableString16 unused; - map_->set_quota(std::numeric_limits::max()); - map_->SetItem(key_string, new_value_string, &unused); - map_->set_quota(kPerStorageAreaQuota); - } - } - - blink::WebStorageEventDispatcher::dispatchLocalStorageEvent( - key_string, old_value.To(), new_value_string, - GURL(origin_.Serialize()), page_url, originating_area); + base::NullableString16 old_value_str(old_value.To(), false); + KeyAddedOrChanged(std::move(key), std::move(new_value), + old_value_str, source); } void LocalStorageCachedArea::KeyDeleted(mojo::Array key, @@ -175,7 +159,7 @@ void LocalStorageCachedArea::KeyDeleted(mojo::Array key, if (areas_.find(storage_area_id) != areas_.end()) { // The source storage area is in this process. originating_area = areas_[storage_area_id]; - } else { + } else if (map_ && !ignore_all_mutations_) { // This was from another process or the storage area is gone. If the former, // remove it from our cache if we haven't already changed it and are waiting // for the confirmation callback. In the latter case, we won't do anything @@ -200,7 +184,7 @@ void LocalStorageCachedArea::AllDeleted(const mojo::String& source) { if (areas_.find(storage_area_id) != areas_.end()) { // The source storage area is in this process. originating_area = areas_[storage_area_id]; - } else { + } else if (map_ && !ignore_all_mutations_) { scoped_refptr old = map_; map_ = new DOMStorageMap(kPerStorageAreaQuota); @@ -223,14 +207,65 @@ void LocalStorageCachedArea::AllDeleted(const mojo::String& source) { originating_area); } +void LocalStorageCachedArea::GetAllComplete(const mojo::String& source) { + // Since the GetAll method is synchronous, we need this asynchronously + // delivered notification to avoid applying changes to the returned array + // that we already have. + if (source.To() == get_all_request_id_) { + DCHECK(ignore_all_mutations_); + DCHECK(!get_all_request_id_.empty()); + ignore_all_mutations_ = false; + get_all_request_id_.clear(); + } +} + +void LocalStorageCachedArea::KeyAddedOrChanged( + mojo::Array key, + mojo::Array new_value, + const base::NullableString16& old_value, + const mojo::String& source) { + GURL page_url; + std::string storage_area_id; + UnpackSource(source, &page_url, &storage_area_id); + + base::string16 key_string = key.To(); + base::string16 new_value_string = new_value.To(); + + blink::WebStorageArea* originating_area = nullptr; + if (areas_.find(storage_area_id) != areas_.end()) { + // The source storage area is in this process. + originating_area = areas_[storage_area_id]; + } else if (map_ && !ignore_all_mutations_) { + // This was from another process or the storage area is gone. If the former, + // apply it to our cache if we haven't already changed it and are waiting + // for the confirmation callback. In the latter case, we won't do anything + // because ignore_key_mutations_ won't be updated until the callback runs. + if (ignore_key_mutations_.find(key_string) != ignore_key_mutations_.end()) { + // We turn off quota checking here to accomodate the over budget allowance + // that's provided in the browser process. + base::NullableString16 unused; + map_->set_quota(std::numeric_limits::max()); + map_->SetItem(key_string, new_value_string, &unused); + map_->set_quota(kPerStorageAreaQuota); + } + } + + blink::WebStorageEventDispatcher::dispatchLocalStorageEvent( + key_string, old_value, new_value_string, + GURL(origin_.Serialize()), page_url, originating_area); + +} + void LocalStorageCachedArea::EnsureLoaded() { if (map_) return; base::TimeTicks before = base::TimeTicks::Now(); + ignore_all_mutations_ = true; + get_all_request_id_ = base::Uint64ToString(base::RandUint64()); leveldb::DatabaseError status = leveldb::DatabaseError::OK; mojo::Array data; - leveldb_->GetAll(binding_.CreateInterfacePtrAndBind(), &status, &data); + leveldb_->GetAll(get_all_request_id_, &status, &data); DOMStorageValuesMap values; for (size_t i = 0; i < data.size(); ++i) { @@ -264,8 +299,8 @@ void LocalStorageCachedArea::EnsureLoaded() { } void LocalStorageCachedArea::OnSetItemComplete(const base::string16& key, - leveldb::DatabaseError result) { - if (result != leveldb::DatabaseError::OK) { + bool success) { + if (!success) { Reset(); return; } @@ -277,22 +312,25 @@ void LocalStorageCachedArea::OnSetItemComplete(const base::string16& key, } void LocalStorageCachedArea::OnRemoveItemComplete( - const base::string16& key, leveldb::DatabaseError result) { - DCHECK_EQ(result, leveldb::DatabaseError::OK); + const base::string16& key, bool success) { + DCHECK(success); auto found = ignore_key_mutations_.find(key); DCHECK(found != ignore_key_mutations_.end()); if (--found->second == 0) ignore_key_mutations_.erase(found); } -void LocalStorageCachedArea::OnClearComplete(leveldb::DatabaseError result) { - DCHECK_EQ(result, leveldb::DatabaseError::OK); +void LocalStorageCachedArea::OnClearComplete(bool success) { + DCHECK(success); + DCHECK(ignore_all_mutations_); + ignore_all_mutations_ = false; } void LocalStorageCachedArea::Reset() { - binding_.Close(); map_ = NULL; ignore_key_mutations_.clear(); + ignore_all_mutations_ = false; + weak_factory_.InvalidateWeakPtrs(); } } // namespace content diff --git a/content/renderer/dom_storage/local_storage_cached_area.h b/content/renderer/dom_storage/local_storage_cached_area.h index 27977699e744fe..316f4e81082c3a 100644 --- a/content/renderer/dom_storage/local_storage_cached_area.h +++ b/content/renderer/dom_storage/local_storage_cached_area.h @@ -65,6 +65,9 @@ class LocalStorageCachedArea : public mojom::LevelDBObserver, ~LocalStorageCachedArea() override; // LevelDBObserver: + void KeyAdded(mojo::Array key, + mojo::Array value, + const mojo::String& source) override; void KeyChanged(mojo::Array key, mojo::Array new_value, mojo::Array old_value, @@ -73,16 +76,21 @@ class LocalStorageCachedArea : public mojom::LevelDBObserver, mojo::Array old_value, const mojo::String& source) override; void AllDeleted(const mojo::String& source) override; + void GetAllComplete(const mojo::String& source) override; + + // Common helper for KeyAdded() and KeyChanged() + void KeyAddedOrChanged(mojo::Array key, + mojo::Array new_value, + const base::NullableString16& old_value, + const mojo::String& source); // Synchronously fetches the origin's local storage data if it hasn't been // fetched already. void EnsureLoaded(); - void OnSetItemComplete(const base::string16& key, - leveldb::DatabaseError result); - void OnRemoveItemComplete(const base::string16& key, - leveldb::DatabaseError result); - void OnClearComplete(leveldb::DatabaseError result); + void OnSetItemComplete(const base::string16& key, bool success); + void OnRemoveItemComplete(const base::string16& key, bool success); + void OnClearComplete(bool success); // Resets the object back to its newly constructed state. void Reset(); @@ -90,10 +98,13 @@ class LocalStorageCachedArea : public mojom::LevelDBObserver, url::Origin origin_; scoped_refptr map_; std::map ignore_key_mutations_; + bool ignore_all_mutations_ = false; + std::string get_all_request_id_; mojom::LevelDBWrapperPtr leveldb_; mojo::Binding binding_; LocalStorageCachedAreas* cached_areas_; std::map areas_; + base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(LocalStorageCachedArea); }; diff --git a/mojo/public/cpp/bindings/array.h b/mojo/public/cpp/bindings/array.h index f522faab532a4d..e9bd6838412e94 100644 --- a/mojo/public/cpp/bindings/array.h +++ b/mojo/public/cpp/bindings/array.h @@ -295,6 +295,16 @@ struct TypeConverter, Array> { } }; +// Less than operator to allow Arrays as keys in std maps and sets. +template +inline bool operator<(const Array& a, const Array& b) { + if (a.is_null()) + return !b.is_null(); + if (b.is_null()) + return false; + return a.storage() < b.storage(); +} + } // namespace mojo #endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_