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