Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "content/renderer/dom_storage/dom_storage_cached_area.h" |
| 6 | |
| 7 | #include "base/basictypes.h" |
| 8 | #include "base/metrics/histogram.h" |
| 9 | #include "base/time/time.h" |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 10 | #include "content/common/dom_storage/dom_storage_map.h" |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 11 | #include "content/renderer/dom_storage/dom_storage_proxy.h" |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 12 | |
| 13 | namespace content { |
| 14 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 15 | DOMStorageCachedArea::DOMStorageCachedArea(int64 namespace_id, |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 16 | const GURL& origin, |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 17 | DOMStorageProxy* proxy) |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 18 | : ignore_all_mutations_(false), |
| 19 | namespace_id_(namespace_id), |
| 20 | origin_(origin), |
| 21 | proxy_(proxy), |
| 22 | weak_factory_(this) {} |
| 23 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 24 | DOMStorageCachedArea::~DOMStorageCachedArea() {} |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 25 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 26 | unsigned DOMStorageCachedArea::GetLength(int connection_id) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 27 | PrimeIfNeeded(connection_id); |
| 28 | return map_->Length(); |
| 29 | } |
| 30 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 31 | base::NullableString16 DOMStorageCachedArea::GetKey(int connection_id, |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 32 | unsigned index) { |
| 33 | PrimeIfNeeded(connection_id); |
| 34 | return map_->Key(index); |
| 35 | } |
| 36 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 37 | base::NullableString16 DOMStorageCachedArea::GetItem( |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 38 | int connection_id, |
| 39 | const base::string16& key) { |
| 40 | PrimeIfNeeded(connection_id); |
| 41 | return map_->GetItem(key); |
| 42 | } |
| 43 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 44 | bool DOMStorageCachedArea::SetItem(int connection_id, |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 45 | const base::string16& key, |
| 46 | const base::string16& value, |
| 47 | const GURL& page_url) { |
| 48 | // A quick check to reject obviously overbudget items to avoid |
| 49 | // the priming the cache. |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 50 | if (key.length() + value.length() > kPerStorageAreaQuota) |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 51 | return false; |
| 52 | |
| 53 | PrimeIfNeeded(connection_id); |
| 54 | base::NullableString16 unused; |
| 55 | if (!map_->SetItem(key, value, &unused)) |
| 56 | return false; |
| 57 | |
| 58 | // Ignore mutations to 'key' until OnSetItemComplete. |
| 59 | ignore_key_mutations_[key]++; |
| 60 | proxy_->SetItem( |
| 61 | connection_id, key, value, page_url, |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 62 | base::Bind(&DOMStorageCachedArea::OnSetItemComplete, |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 63 | weak_factory_.GetWeakPtr(), key)); |
| 64 | return true; |
| 65 | } |
| 66 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 67 | void DOMStorageCachedArea::RemoveItem(int connection_id, |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 68 | const base::string16& key, |
| 69 | const GURL& page_url) { |
| 70 | PrimeIfNeeded(connection_id); |
| 71 | base::string16 unused; |
| 72 | if (!map_->RemoveItem(key, &unused)) |
| 73 | return; |
| 74 | |
| 75 | // Ignore mutations to 'key' until OnRemoveItemComplete. |
| 76 | ignore_key_mutations_[key]++; |
| 77 | proxy_->RemoveItem( |
| 78 | connection_id, key, page_url, |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 79 | base::Bind(&DOMStorageCachedArea::OnRemoveItemComplete, |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 80 | weak_factory_.GetWeakPtr(), key)); |
| 81 | } |
| 82 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 83 | void DOMStorageCachedArea::Clear(int connection_id, const GURL& page_url) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 84 | // No need to prime the cache in this case. |
| 85 | Reset(); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 86 | map_ = new DOMStorageMap(kPerStorageAreaQuota); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 87 | |
| 88 | // Ignore all mutations until OnClearComplete time. |
| 89 | ignore_all_mutations_ = true; |
| 90 | proxy_->ClearArea(connection_id, |
| 91 | page_url, |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 92 | base::Bind(&DOMStorageCachedArea::OnClearComplete, |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 93 | weak_factory_.GetWeakPtr())); |
| 94 | } |
| 95 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 96 | void DOMStorageCachedArea::ApplyMutation( |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 97 | const base::NullableString16& key, |
| 98 | const base::NullableString16& new_value) { |
| 99 | if (!map_.get() || ignore_all_mutations_) |
| 100 | return; |
| 101 | |
| 102 | if (key.is_null()) { |
| 103 | // It's a clear event. |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 104 | scoped_refptr<DOMStorageMap> old = map_; |
| 105 | map_ = new DOMStorageMap(kPerStorageAreaQuota); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 106 | |
| 107 | // We have to retain local additions which happened after this |
| 108 | // clear operation from another process. |
| 109 | std::map<base::string16, int>::iterator iter = |
| 110 | ignore_key_mutations_.begin(); |
| 111 | while (iter != ignore_key_mutations_.end()) { |
| 112 | base::NullableString16 value = old->GetItem(iter->first); |
| 113 | if (!value.is_null()) { |
| 114 | base::NullableString16 unused; |
| 115 | map_->SetItem(iter->first, value.string(), &unused); |
| 116 | } |
| 117 | ++iter; |
| 118 | } |
| 119 | return; |
| 120 | } |
| 121 | |
| 122 | // We have to retain local changes. |
| 123 | if (should_ignore_key_mutation(key.string())) |
| 124 | return; |
| 125 | |
| 126 | if (new_value.is_null()) { |
| 127 | // It's a remove item event. |
| 128 | base::string16 unused; |
| 129 | map_->RemoveItem(key.string(), &unused); |
| 130 | return; |
| 131 | } |
| 132 | |
| 133 | // It's a set item event. |
| 134 | // We turn off quota checking here to accomodate the over budget |
| 135 | // allowance that's provided in the browser process. |
| 136 | base::NullableString16 unused; |
| 137 | map_->set_quota(kint32max); |
| 138 | map_->SetItem(key.string(), new_value.string(), &unused); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 139 | map_->set_quota(kPerStorageAreaQuota); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 140 | } |
| 141 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 142 | size_t DOMStorageCachedArea::MemoryBytesUsedByCache() const { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 143 | return map_.get() ? map_->bytes_used() : 0; |
| 144 | } |
| 145 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 146 | void DOMStorageCachedArea::Prime(int connection_id) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 147 | DCHECK(!map_.get()); |
| 148 | |
| 149 | // The LoadArea method is actually synchronous, but we have to |
| 150 | // wait for an asyncly delivered message to know when incoming |
| 151 | // mutation events should be applied. Our valuemap is plucked |
| 152 | // from ipc stream out of order, mutations in front if it need |
| 153 | // to be ignored. |
| 154 | |
| 155 | // Ignore all mutations until OnLoadComplete time. |
| 156 | ignore_all_mutations_ = true; |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 157 | DOMStorageValuesMap values; |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 158 | base::TimeTicks before = base::TimeTicks::Now(); |
| 159 | proxy_->LoadArea(connection_id, |
| 160 | &values, |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 161 | base::Bind(&DOMStorageCachedArea::OnLoadComplete, |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 162 | weak_factory_.GetWeakPtr())); |
| 163 | base::TimeDelta time_to_prime = base::TimeTicks::Now() - before; |
| 164 | // Keeping this histogram named the same (without the ForRenderer suffix) |
| 165 | // to maintain histogram continuity. |
| 166 | UMA_HISTOGRAM_TIMES("LocalStorage.TimeToPrimeLocalStorage", |
| 167 | time_to_prime); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 168 | map_ = new DOMStorageMap(kPerStorageAreaQuota); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 169 | map_->SwapValues(&values); |
| 170 | |
| 171 | size_t local_storage_size_kb = map_->bytes_used() / 1024; |
| 172 | // Track localStorage size, from 0-6MB. Note that the maximum size should be |
| 173 | // 5MB, but we add some slop since we want to make sure the max size is always |
| 174 | // above what we see in practice, since histograms can't change. |
| 175 | UMA_HISTOGRAM_CUSTOM_COUNTS("LocalStorage.RendererLocalStorageSizeInKB", |
| 176 | local_storage_size_kb, |
| 177 | 0, 6 * 1024, 50); |
| 178 | if (local_storage_size_kb < 100) { |
| 179 | UMA_HISTOGRAM_TIMES( |
| 180 | "LocalStorage.RendererTimeToPrimeLocalStorageUnder100KB", |
| 181 | time_to_prime); |
| 182 | } else if (local_storage_size_kb < 1000) { |
| 183 | UMA_HISTOGRAM_TIMES( |
| 184 | "LocalStorage.RendererTimeToPrimeLocalStorage100KBTo1MB", |
| 185 | time_to_prime); |
| 186 | } else { |
| 187 | UMA_HISTOGRAM_TIMES( |
| 188 | "LocalStorage.RendererTimeToPrimeLocalStorage1MBTo5MB", |
| 189 | time_to_prime); |
| 190 | } |
| 191 | } |
| 192 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 193 | void DOMStorageCachedArea::Reset() { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 194 | map_ = NULL; |
| 195 | weak_factory_.InvalidateWeakPtrs(); |
| 196 | ignore_key_mutations_.clear(); |
| 197 | ignore_all_mutations_ = false; |
| 198 | } |
| 199 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 200 | void DOMStorageCachedArea::OnLoadComplete(bool success) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 201 | DCHECK(success); |
| 202 | DCHECK(ignore_all_mutations_); |
| 203 | ignore_all_mutations_ = false; |
| 204 | } |
| 205 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 206 | void DOMStorageCachedArea::OnSetItemComplete(const base::string16& key, |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 207 | bool success) { |
| 208 | if (!success) { |
| 209 | Reset(); |
| 210 | return; |
| 211 | } |
| 212 | std::map<base::string16, int>::iterator found = |
| 213 | ignore_key_mutations_.find(key); |
| 214 | DCHECK(found != ignore_key_mutations_.end()); |
| 215 | if (--found->second == 0) |
| 216 | ignore_key_mutations_.erase(found); |
| 217 | } |
| 218 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 219 | void DOMStorageCachedArea::OnRemoveItemComplete(const base::string16& key, |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 220 | bool success) { |
| 221 | DCHECK(success); |
| 222 | std::map<base::string16, int>::iterator found = |
| 223 | ignore_key_mutations_.find(key); |
| 224 | DCHECK(found != ignore_key_mutations_.end()); |
| 225 | if (--found->second == 0) |
| 226 | ignore_key_mutations_.erase(found); |
| 227 | } |
| 228 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 229 | void DOMStorageCachedArea::OnClearComplete(bool success) { |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 230 | DCHECK(success); |
| 231 | DCHECK(ignore_all_mutations_); |
| 232 | ignore_all_mutations_ = false; |
| 233 | } |
| 234 | |
| 235 | } // namespace content |