OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/renderer/dom_storage/local_storage_cached_area.h" | 5 #include "content/renderer/dom_storage/local_storage_cached_area.h" |
6 | 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/metrics/histogram_macros.h" |
| 9 #include "base/strings/string_split.h" |
| 10 #include "base/time/time.h" |
| 11 #include "content/common/dom_storage/dom_storage_map.h" |
7 #include "content/common/storage_partition_service.mojom.h" | 12 #include "content/common/storage_partition_service.mojom.h" |
| 13 #include "content/renderer/dom_storage/local_storage_area.h" |
8 #include "content/renderer/dom_storage/local_storage_cached_areas.h" | 14 #include "content/renderer/dom_storage/local_storage_cached_areas.h" |
| 15 #include "mojo/common/common_type_converters.h" |
| 16 #include "third_party/WebKit/public/platform/WebURL.h" |
| 17 #include "third_party/WebKit/public/web/WebStorageEventDispatcher.h" |
| 18 #include "url/gurl.h" |
9 | 19 |
10 namespace content { | 20 namespace content { |
11 | 21 |
| 22 // These methods are used to pack and unpack the page_url/storage_area_id into |
| 23 // source strings to/from the browser. |
| 24 std::string PackSource(const GURL& page_url, |
| 25 const std::string& storage_area_id) { |
| 26 return page_url.spec() + "\n" + storage_area_id; |
| 27 } |
| 28 |
| 29 void UnpackSource(const mojo::String& source, |
| 30 GURL* page_url, |
| 31 std::string* storage_area_id) { |
| 32 std::vector<std::string> result = base::SplitString( |
| 33 source.To<std::string>(), "\n", base::KEEP_WHITESPACE, |
| 34 base::SPLIT_WANT_ALL); |
| 35 DCHECK_EQ(result.size(), 2u); |
| 36 *page_url = GURL(result[0]); |
| 37 *storage_area_id = result[1]; |
| 38 } |
| 39 |
12 LocalStorageCachedArea::LocalStorageCachedArea( | 40 LocalStorageCachedArea::LocalStorageCachedArea( |
13 const url::Origin& origin, | 41 const url::Origin& origin, |
14 StoragePartitionService* storage_partition_service, | 42 StoragePartitionService* storage_partition_service, |
15 LocalStorageCachedAreas* cached_areas) | 43 LocalStorageCachedAreas* cached_areas) |
16 : loaded_(false), origin_(origin), binding_(this), | 44 : loaded_(false), origin_(origin), binding_(this), |
17 cached_areas_(cached_areas) { | 45 cached_areas_(cached_areas) { |
18 storage_partition_service->OpenLocalStorage( | 46 storage_partition_service->OpenLocalStorage( |
19 origin_, mojo::GetProxy(&leveldb_)); | 47 origin_, mojo::GetProxy(&leveldb_)); |
20 } | 48 } |
21 | 49 |
22 LocalStorageCachedArea::~LocalStorageCachedArea() { | 50 LocalStorageCachedArea::~LocalStorageCachedArea() { |
23 cached_areas_->LocalStorageCacheAreaClosed(this); | 51 cached_areas_->CacheAreaClosed(this); |
24 } | 52 } |
25 | 53 |
26 unsigned LocalStorageCachedArea::GetLength() { | 54 unsigned LocalStorageCachedArea::GetLength() { |
27 EnsureLoaded(); | 55 EnsureLoaded(); |
28 return 0u; | 56 return map_->Length(); |
29 } | 57 } |
30 | 58 |
31 base::NullableString16 LocalStorageCachedArea::GetKey(unsigned index) { | 59 base::NullableString16 LocalStorageCachedArea::GetKey(unsigned index) { |
32 EnsureLoaded(); | 60 EnsureLoaded(); |
33 return base::NullableString16(); | 61 return map_->Key(index); |
34 } | 62 } |
35 | 63 |
36 base::NullableString16 LocalStorageCachedArea::GetItem( | 64 base::NullableString16 LocalStorageCachedArea::GetItem( |
37 const base::string16& key) { | 65 const base::string16& key) { |
38 EnsureLoaded(); | 66 EnsureLoaded(); |
39 return base::NullableString16(); | 67 return map_->GetItem(key); |
40 } | 68 } |
41 | 69 |
42 bool LocalStorageCachedArea::SetItem(const base::string16& key, | 70 bool LocalStorageCachedArea::SetItem(const base::string16& key, |
43 const base::string16& value, | 71 const base::string16& value, |
44 const GURL& page_url) { | 72 const GURL& page_url, |
| 73 const std::string& storage_area_id) { |
| 74 // A quick check to reject obviously overbudget items to avoid priming the |
| 75 // cache. |
| 76 if (key.length() + value.length() > kPerStorageAreaQuota) |
| 77 return false; |
| 78 |
45 EnsureLoaded(); | 79 EnsureLoaded(); |
46 return false; | 80 base::NullableString16 unused; |
| 81 if (!map_->SetItem(key, value, &unused)) |
| 82 return false; |
| 83 |
| 84 // Ignore mutations to |key| until OnSetItemComplete. |
| 85 ignore_key_mutations_[key]++; |
| 86 leveldb_->Put(mojo::Array<uint8_t>::From(key), |
| 87 mojo::Array<uint8_t>::From(value), |
| 88 PackSource(page_url, storage_area_id), |
| 89 base::Bind(&LocalStorageCachedArea::OnSetItemComplete, |
| 90 base::Unretained(this), key)); |
| 91 return true; |
47 } | 92 } |
48 | 93 |
49 void LocalStorageCachedArea::RemoveItem(const base::string16& key, | 94 void LocalStorageCachedArea::RemoveItem(const base::string16& key, |
50 const GURL& page_url) { | 95 const GURL& page_url, |
| 96 const std::string& storage_area_id) { |
51 EnsureLoaded(); | 97 EnsureLoaded(); |
| 98 base::string16 unused; |
| 99 if (!map_->RemoveItem(key, &unused)) |
| 100 return; |
| 101 |
| 102 // Ignore mutations to |key| until OnRemoveItemComplete. |
| 103 ignore_key_mutations_[key]++; |
| 104 leveldb_->Delete(mojo::Array<uint8_t>::From(key), |
| 105 PackSource(page_url, storage_area_id), |
| 106 base::Bind(&LocalStorageCachedArea::OnRemoveItemComplete, |
| 107 base::Unretained(this), key)); |
52 } | 108 } |
53 | 109 |
54 void LocalStorageCachedArea::Clear(const GURL& page_url) { | 110 void LocalStorageCachedArea::Clear(const GURL& page_url, |
| 111 const std::string& storage_area_id) { |
55 // No need to prime the cache in this case. | 112 // No need to prime the cache in this case. |
56 | 113 |
| 114 Reset(); |
| 115 map_ = new DOMStorageMap(kPerStorageAreaQuota); |
57 binding_.Close(); | 116 binding_.Close(); |
58 // TODO: | 117 |
59 // binding_.CreateInterfacePtrAndBind() | 118 leveldb_->DeleteAll(binding_.CreateInterfacePtrAndBind(), |
| 119 PackSource(page_url, storage_area_id), |
| 120 base::Bind(&LocalStorageCachedArea::OnClearComplete, |
| 121 base::Unretained(this))); |
| 122 } |
| 123 |
| 124 void LocalStorageCachedArea::AreaCreated(LocalStorageArea* area) { |
| 125 areas_[area->id()] = area; |
| 126 } |
| 127 |
| 128 void LocalStorageCachedArea::AreaDestroyed(LocalStorageArea* area) { |
| 129 areas_.erase(area->id()); |
60 } | 130 } |
61 | 131 |
62 void LocalStorageCachedArea::KeyChanged(mojo::Array<uint8_t> key, | 132 void LocalStorageCachedArea::KeyChanged(mojo::Array<uint8_t> key, |
63 mojo::Array<uint8_t> new_value, | 133 mojo::Array<uint8_t> new_value, |
64 mojo::Array<uint8_t> old_value, | 134 mojo::Array<uint8_t> old_value, |
65 const mojo::String& source) { | 135 const mojo::String& source) { |
| 136 GURL page_url; |
| 137 std::string storage_area_id; |
| 138 UnpackSource(source, &page_url, &storage_area_id); |
| 139 |
| 140 base::string16 key_string = key.To<base::string16>(); |
| 141 base::string16 new_value_string = new_value.To<base::string16>(); |
| 142 |
| 143 blink::WebStorageArea* originating_area = nullptr; |
| 144 if (areas_.find(storage_area_id) != areas_.end()) { |
| 145 // The source storage area is in this process. |
| 146 originating_area = areas_[storage_area_id]; |
| 147 } else { |
| 148 // This was from another process or the storage area is gone. If the former, |
| 149 // apply it to our cache if we haven't already changed it and are waiting |
| 150 // for the confirmation callback. In the latter case, we won't do anything |
| 151 // because ignore_key_mutations_ won't be updated until the callback runs. |
| 152 if (ignore_key_mutations_.find(key_string) != ignore_key_mutations_.end()) { |
| 153 // We turn off quota checking here to accomodate the over budget allowance |
| 154 // that's provided in the browser process. |
| 155 base::NullableString16 unused; |
| 156 map_->set_quota(std::numeric_limits<int32_t>::max()); |
| 157 map_->SetItem(key_string, new_value_string, &unused); |
| 158 map_->set_quota(kPerStorageAreaQuota); |
| 159 } |
| 160 } |
| 161 |
| 162 blink::WebStorageEventDispatcher::dispatchLocalStorageEvent( |
| 163 key_string, old_value.To<base::string16>(), new_value_string, |
| 164 GURL(origin_.Serialize()), page_url, originating_area); |
66 } | 165 } |
67 | 166 |
68 void LocalStorageCachedArea::KeyDeleted(mojo::Array<uint8_t> key, | 167 void LocalStorageCachedArea::KeyDeleted(mojo::Array<uint8_t> key, |
| 168 mojo::Array<uint8_t> old_value, |
69 const mojo::String& source) { | 169 const mojo::String& source) { |
| 170 GURL page_url; |
| 171 std::string storage_area_id; |
| 172 UnpackSource(source, &page_url, &storage_area_id); |
| 173 |
| 174 base::string16 key_string = key.To<base::string16>(); |
| 175 |
| 176 blink::WebStorageArea* originating_area = nullptr; |
| 177 if (areas_.find(storage_area_id) != areas_.end()) { |
| 178 // The source storage area is in this process. |
| 179 originating_area = areas_[storage_area_id]; |
| 180 } else { |
| 181 // This was from another process or the storage area is gone. If the former, |
| 182 // remove it from our cache if we haven't already changed it and are waiting |
| 183 // for the confirmation callback. In the latter case, we won't do anything |
| 184 // because ignore_key_mutations_ won't be updated until the callback runs. |
| 185 if (ignore_key_mutations_.find(key_string) != ignore_key_mutations_.end()) { |
| 186 base::string16 unused; |
| 187 map_->RemoveItem(key_string, &unused); |
| 188 } |
| 189 } |
| 190 |
| 191 blink::WebStorageEventDispatcher::dispatchLocalStorageEvent( |
| 192 key_string, old_value.To<base::string16>(), base::NullableString16(), |
| 193 GURL(origin_.Serialize()), page_url, originating_area); |
70 } | 194 } |
71 | 195 |
72 void LocalStorageCachedArea::AllDeleted(const mojo::String& source) { | 196 void LocalStorageCachedArea::AllDeleted(const mojo::String& source) { |
| 197 GURL page_url; |
| 198 std::string storage_area_id; |
| 199 UnpackSource(source, &page_url, &storage_area_id); |
| 200 |
| 201 blink::WebStorageArea* originating_area = nullptr; |
| 202 if (areas_.find(storage_area_id) != areas_.end()) { |
| 203 // The source storage area is in this process. |
| 204 originating_area = areas_[storage_area_id]; |
| 205 } else { |
| 206 scoped_refptr<DOMStorageMap> old = map_; |
| 207 map_ = new DOMStorageMap(kPerStorageAreaQuota); |
| 208 |
| 209 // We have to retain local additions which happened after this clear |
| 210 // operation from another process. |
| 211 auto iter = ignore_key_mutations_.begin(); |
| 212 while (iter != ignore_key_mutations_.end()) { |
| 213 base::NullableString16 value = old->GetItem(iter->first); |
| 214 if (!value.is_null()) { |
| 215 base::NullableString16 unused; |
| 216 map_->SetItem(iter->first, value.string(), &unused); |
| 217 } |
| 218 ++iter; |
| 219 } |
| 220 } |
| 221 |
| 222 blink::WebStorageEventDispatcher::dispatchLocalStorageEvent( |
| 223 base::NullableString16(), base::NullableString16(), |
| 224 base::NullableString16(), GURL(origin_.Serialize()), page_url, |
| 225 originating_area); |
73 } | 226 } |
74 | 227 |
75 void LocalStorageCachedArea::EnsureLoaded() { | 228 void LocalStorageCachedArea::EnsureLoaded() { |
76 if (loaded_) | 229 if (loaded_) |
77 return; | 230 return; |
78 | 231 |
79 loaded_ = true; | 232 loaded_ = true; |
| 233 base::TimeTicks before = base::TimeTicks::Now(); |
80 leveldb::DatabaseError status = leveldb::DatabaseError::OK; | 234 leveldb::DatabaseError status = leveldb::DatabaseError::OK; |
81 mojo::Array<content::KeyValuePtr> data; | 235 mojo::Array<content::KeyValuePtr> data; |
82 leveldb_->GetAll(binding_.CreateInterfacePtrAndBind(), &status, &data); | 236 leveldb_->GetAll(binding_.CreateInterfacePtrAndBind(), &status, &data); |
| 237 |
| 238 DOMStorageValuesMap values; |
| 239 for (size_t i = 0; i < data.size(); ++i) { |
| 240 values[data[i]->key.To<base::string16>()] = |
| 241 base::NullableString16(data[i]->value.To<base::string16>(), false); |
| 242 } |
| 243 |
| 244 map_ = new DOMStorageMap(kPerStorageAreaQuota); |
| 245 map_->SwapValues(&values); |
| 246 |
| 247 base::TimeDelta time_to_prime = base::TimeTicks::Now() - before; |
| 248 UMA_HISTOGRAM_TIMES("LocalStorage.MojoTimeToPrime", time_to_prime); |
| 249 |
| 250 size_t local_storage_size_kb = map_->bytes_used() / 1024; |
| 251 // Track localStorage size, from 0-6MB. Note that the maximum size should be |
| 252 // 5MB, but we add some slop since we want to make sure the max size is always |
| 253 // above what we see in practice, since histograms can't change. |
| 254 UMA_HISTOGRAM_CUSTOM_COUNTS("LocalStorage.MojoSizeInKB", |
| 255 local_storage_size_kb, |
| 256 0, 6 * 1024, 50); |
| 257 if (local_storage_size_kb < 100) { |
| 258 UMA_HISTOGRAM_TIMES("LocalStorage.MojoTimeToPrimeForUnder100KB", |
| 259 time_to_prime); |
| 260 } else if (local_storage_size_kb < 1000) { |
| 261 UMA_HISTOGRAM_TIMES("LocalStorage.MojoTimeToPrimeFor100KBTo1MB", |
| 262 time_to_prime); |
| 263 } else { |
| 264 UMA_HISTOGRAM_TIMES("LocalStorage.MojoTimeToPrimeFor1MBTo5MB", |
| 265 time_to_prime); |
| 266 } |
| 267 } |
| 268 |
| 269 void LocalStorageCachedArea::OnSetItemComplete(const base::string16& key, |
| 270 leveldb::DatabaseError result) { |
| 271 if (result != leveldb::DatabaseError::OK) { |
| 272 Reset(); |
| 273 return; |
| 274 } |
| 275 |
| 276 auto found = ignore_key_mutations_.find(key); |
| 277 DCHECK(found != ignore_key_mutations_.end()); |
| 278 if (--found->second == 0) |
| 279 ignore_key_mutations_.erase(found); |
| 280 } |
| 281 |
| 282 void LocalStorageCachedArea::OnRemoveItemComplete( |
| 283 const base::string16& key, leveldb::DatabaseError result) { |
| 284 DCHECK_EQ(result, leveldb::DatabaseError::OK); |
| 285 auto found = ignore_key_mutations_.find(key); |
| 286 DCHECK(found != ignore_key_mutations_.end()); |
| 287 if (--found->second == 0) |
| 288 ignore_key_mutations_.erase(found); |
| 289 } |
| 290 |
| 291 void LocalStorageCachedArea::OnClearComplete(leveldb::DatabaseError result) { |
| 292 DCHECK_EQ(result, leveldb::DatabaseError::OK); |
| 293 } |
| 294 |
| 295 void LocalStorageCachedArea::Reset() { |
| 296 map_ = NULL; |
| 297 ignore_key_mutations_.clear(); |
83 } | 298 } |
84 | 299 |
85 } // namespace content | 300 } // namespace content |
OLD | NEW |