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_