Chromium Code Reviews| Index: content/renderer/dom_storage/local_storage_cached_area.cc |
| diff --git a/content/renderer/dom_storage/local_storage_cached_area.cc b/content/renderer/dom_storage/local_storage_cached_area.cc |
| index 45336e2afcc8efc058f122edb3cf918840edfe72..94e03135e10eed3ab21999b4fc3e13ae4bb9ac7b 100644 |
| --- a/content/renderer/dom_storage/local_storage_cached_area.cc |
| +++ b/content/renderer/dom_storage/local_storage_cached_area.cc |
| @@ -4,11 +4,37 @@ |
| #include "content/renderer/dom_storage/local_storage_cached_area.h" |
| +#include "base/bind.h" |
| +#include "base/strings/string_split.h" |
| +#include "content/common/dom_storage/dom_storage_map.h" |
| #include "content/common/storage_partition_service.mojom.h" |
| +#include "content/renderer/dom_storage/local_storage_area.h" |
| #include "content/renderer/dom_storage/local_storage_cached_areas.h" |
| +#include "mojo/common/common_type_converters.h" |
| +#include "third_party/WebKit/public/platform/WebURL.h" |
| +#include "third_party/WebKit/public/web/WebStorageEventDispatcher.h" |
| +#include "url/gurl.h" |
| namespace content { |
| +// These methods are used to pack and unpack the page_url/storage_area_id into |
| +// source strings to/from the browser. |
| +std::string PackSource(const GURL& page_url, |
| + const std::string& storage_area_id) { |
| + return page_url.spec() + "\n" + storage_area_id; |
|
dcheng
2016/03/18 06:35:29
Can GURL::spec() contain raw newlines?
jam
2016/03/18 16:48:33
no (that's why i used it. it's one of the characte
|
| +} |
| + |
| +void UnpackSource(const mojo::String& source, |
| + GURL* page_url, |
| + std::string* storage_area_id) { |
| + std::vector<std::string> result = base::SplitString( |
| + source.To<std::string>(), "\n", base::KEEP_WHITESPACE, |
| + base::SPLIT_WANT_ALL); |
| + DCHECK_EQ(result.size(), 2u); |
| + *page_url = GURL(result[0]); |
| + *storage_area_id = result[1]; |
| +} |
| + |
| LocalStorageCachedArea::LocalStorageCachedArea( |
| const url::Origin& origin, |
| StoragePartitionService* storage_partition_service, |
| @@ -25,51 +51,173 @@ LocalStorageCachedArea::~LocalStorageCachedArea() { |
| unsigned LocalStorageCachedArea::GetLength() { |
| EnsureLoaded(); |
| - return 0u; |
| + return map_->Length(); |
| } |
| base::NullableString16 LocalStorageCachedArea::GetKey(unsigned index) { |
| EnsureLoaded(); |
| - return base::NullableString16(); |
| + return map_->Key(index); |
| } |
| base::NullableString16 LocalStorageCachedArea::GetItem( |
| const base::string16& key) { |
| EnsureLoaded(); |
| - return base::NullableString16(); |
| + return map_->GetItem(key); |
| } |
|
michaeln
2016/03/18 01:05:16
do we care about retaining this behavior?
// Su
jam
2016/03/18 16:48:33
good question. i should have called it out that i
michaeln
2016/03/18 22:01:14
per our hallway discussion... nevermind :)
mojo h
|
| bool LocalStorageCachedArea::SetItem(const base::string16& key, |
| const base::string16& value, |
| - const GURL& page_url) { |
| + const GURL& page_url, |
| + const std::string& storage_area_id) { |
| + // A quick check to reject obviously overbudget items to avoid priming the |
| + // cache. |
| + if (key.length() + value.length() > kPerStorageAreaQuota) |
| + return false; |
| + |
| EnsureLoaded(); |
| - return false; |
| + base::NullableString16 unused; |
| + if (!map_->SetItem(key, value, &unused)) |
| + return false; |
| + |
| + // Ignore mutations to 'key' until OnSetItemComplete. |
| + ignore_key_mutations_[key]++; |
| + leveldb_->Put(mojo::Array<uint8_t>::From(key), |
| + mojo::Array<uint8_t>::From(value), |
| + PackSource(page_url, storage_area_id), |
| + base::Bind(&LocalStorageCachedArea::OnSetItemComplete, |
| + base::Unretained(this), key)); |
| + return true; |
| } |
| void LocalStorageCachedArea::RemoveItem(const base::string16& key, |
| - const GURL& page_url) { |
| + const GURL& page_url, |
| + const std::string& storage_area_id) { |
| EnsureLoaded(); |
| + base::string16 unused; |
| + if (!map_->RemoveItem(key, &unused)) |
| + return; |
| + |
| + // Ignore mutations to 'key' until OnRemoveItemComplete. |
| + ignore_key_mutations_[key]++; |
| + leveldb_->Delete(mojo::Array<uint8_t>::From(key), |
| + PackSource(page_url, storage_area_id), |
| + base::Bind(&LocalStorageCachedArea::OnRemoveItemComplete, |
| + base::Unretained(this), key)); |
| } |
| -void LocalStorageCachedArea::Clear(const GURL& page_url) { |
| +void LocalStorageCachedArea::Clear(const GURL& page_url, |
| + const std::string& storage_area_id) { |
| // No need to prime the cache in this case. |
| + Reset(); |
| + map_ = new DOMStorageMap(kPerStorageAreaQuota); |
| binding_.Close(); |
| - // TODO: |
| - // binding_.CreateInterfacePtrAndBind() |
| + |
| + leveldb_->DeleteAll(binding_.CreateInterfacePtrAndBind(), |
| + PackSource(page_url, storage_area_id), |
| + base::Bind(&LocalStorageCachedArea::OnClearComplete, |
| + base::Unretained(this))); |
| +} |
| + |
| +void LocalStorageCachedArea::LocalStorageAreaCreated(LocalStorageArea* area) { |
| + areas_[area->id()] = area; |
| +} |
| + |
| +void LocalStorageCachedArea::LocalStorageAreaDestroyed(LocalStorageArea* area) { |
| + areas_.erase(area->id()); |
|
michaeln
2016/03/18 01:05:17
Where is the LocalStorageCachedArea deleted?
I th
jam
2016/03/18 16:48:33
ah I forgot to add that (hard to remember this stu
|
| } |
| void LocalStorageCachedArea::KeyChanged(mojo::Array<uint8_t> key, |
| mojo::Array<uint8_t> new_value, |
| mojo::Array<uint8_t> 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>(); |
| + base::string16 new_value_string = new_value.To<base::string16>(); |
| + |
| + 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, so apply it to our cache if we haven't |
| + // already changed it and are waiting for the confirmation callback. |
|
michaeln
2016/03/18 01:05:17
i'm not sure this holds true if the originating wa
jam
2016/03/18 16:48:33
this is covered by the fact that observers are cal
michaeln
2016/03/18 22:01:14
ah, yes, i see!
ok, please update the comment to
jam
2016/03/18 22:15:01
Done.
|
| + 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<int32_t>::max()); |
| + map_->SetItem(key_string, new_value_string, &unused); |
| + map_->set_quota(kPerStorageAreaQuota); |
| + } |
| + } |
| + |
| + blink::WebStorageEventDispatcher::dispatchLocalStorageEvent( |
| + key_string, old_value.To<base::string16>(), new_value_string, |
| + GURL(origin_.Serialize()), page_url, originating_area); |
| } |
| void LocalStorageCachedArea::KeyDeleted(mojo::Array<uint8_t> key, |
| + mojo::Array<uint8_t> 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>(); |
| + |
| + 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, so remove it from our cache if we haven't |
| + // already changed it and are waiting for the confirmation callback. |
| + if (ignore_key_mutations_.find(key_string) != ignore_key_mutations_.end()) { |
| + base::string16 unused; |
| + map_->RemoveItem(key_string, &unused); |
| + } |
| + } |
| + |
| + blink::WebStorageEventDispatcher::dispatchLocalStorageEvent( |
| + key_string, old_value.To<base::string16>(), base::NullableString16(), |
| + GURL(origin_.Serialize()), page_url, originating_area); |
| } |
| void LocalStorageCachedArea::AllDeleted(const mojo::String& source) { |
| + GURL page_url; |
| + std::string storage_area_id; |
| + UnpackSource(source, &page_url, &storage_area_id); |
| + |
| + 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 { |
| + scoped_refptr<DOMStorageMap> old = map_; |
| + map_ = new DOMStorageMap(kPerStorageAreaQuota); |
| + |
| + // We have to retain local additions which happened after this clear |
| + // operation from another process. |
| + std::map<base::string16, int>::iterator iter = |
| + ignore_key_mutations_.begin(); |
| + while (iter != ignore_key_mutations_.end()) { |
| + base::NullableString16 value = old->GetItem(iter->first); |
| + if (!value.is_null()) { |
| + base::NullableString16 unused; |
| + map_->SetItem(iter->first, value.string(), &unused); |
| + } |
| + ++iter; |
| + } |
| + } |
| + |
| + blink::WebStorageEventDispatcher::dispatchLocalStorageEvent( |
| + base::NullableString16(), base::NullableString16(), |
| + base::NullableString16(), GURL(origin_.Serialize()), page_url, |
| + originating_area); |
| } |
| void LocalStorageCachedArea::EnsureLoaded() { |
| @@ -80,6 +228,47 @@ void LocalStorageCachedArea::EnsureLoaded() { |
| leveldb::DatabaseError status = leveldb::DatabaseError::OK; |
| mojo::Array<content::KeyValuePtr> data; |
| leveldb_->GetAll(binding_.CreateInterfacePtrAndBind(), &status, &data); |
| + |
| + DOMStorageValuesMap values; |
| + for (size_t i = 0; i < data.size(); ++i) { |
| + values[data[i]->key.To<base::string16>()] = |
| + base::NullableString16(data[i]->value.To<base::string16>(), false); |
| + } |
| + |
| + map_ = new DOMStorageMap(kPerStorageAreaQuota); |
| + map_->SwapValues(&values); |
|
michaeln
2016/03/18 01:05:17
wdyt about creating parallel TimeToPrime uma stats
jam
2016/03/18 16:48:33
good idea, done
|
| +} |
| + |
| +void LocalStorageCachedArea::OnSetItemComplete(const base::string16& key, |
| + leveldb::DatabaseError result) { |
| + if (result != leveldb::DatabaseError::OK) { |
| + Reset(); |
| + return; |
| + } |
| + std::map<base::string16, int>::iterator found = |
|
michaeln
2016/03/18 01:05:17
maybe use auto here for readability?
jam
2016/03/18 16:48:33
Done.
|
| + ignore_key_mutations_.find(key); |
| + DCHECK(found != ignore_key_mutations_.end()); |
| + if (--found->second == 0) |
| + ignore_key_mutations_.erase(found); |
| +} |
| + |
| +void LocalStorageCachedArea::OnRemoveItemComplete( |
|
michaeln
2016/03/18 01:05:17
looks like we could share code for OnSet and OnRem
jam
2016/03/18 16:48:33
it's 3 lines ignoring the dcheck; i didn't think i
|
| + const base::string16& key, leveldb::DatabaseError result) { |
| + DCHECK_EQ(result, leveldb::DatabaseError::OK); |
| + std::map<base::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 LocalStorageCachedArea::OnClearComplete(leveldb::DatabaseError result) { |
|
michaeln
2016/03/18 01:05:17
maybe we don't need this callback, i think recreat
jam
2016/03/18 16:48:33
i kept it just so we can have a DCHECK during test
|
| + DCHECK_EQ(result, leveldb::DatabaseError::OK); |
| +} |
| + |
| +void LocalStorageCachedArea::Reset() { |
| + map_ = NULL; |
| + ignore_key_mutations_.clear(); |
| } |
| } // namespace content |