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.
5 #include "content/renderer/dom_storage/dom_storage_cached_area.h"
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"
17 static const int kMaxLogGetMessagesToSend
= 16 * 1024;
21 DOMStorageCachedArea::DOMStorageCachedArea(int64 namespace_id
,
23 DOMStorageProxy
* proxy
)
24 : ignore_all_mutations_(false),
25 namespace_id_(namespace_id
),
28 remaining_log_get_messages_(0),
29 weak_factory_(this) {}
31 DOMStorageCachedArea::~DOMStorageCachedArea() {}
33 unsigned DOMStorageCachedArea::GetLength(int connection_id
) {
34 PrimeIfNeeded(connection_id
);
35 return map_
->Length();
38 base::NullableString16
DOMStorageCachedArea::GetKey(int connection_id
,
40 PrimeIfNeeded(connection_id
);
41 return map_
->Key(index
);
44 base::NullableString16
DOMStorageCachedArea::GetItem(
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
);
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
)
65 PrimeIfNeeded(connection_id
);
66 base::NullableString16 unused
;
67 if (!map_
->SetItem(key
, value
, &unused
))
70 // Ignore mutations to 'key' until OnSetItemComplete.
71 ignore_key_mutations_
[key
]++;
73 connection_id
, key
, value
, page_url
,
74 base::Bind(&DOMStorageCachedArea::OnSetItemComplete
,
75 weak_factory_
.GetWeakPtr(), key
));
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
))
87 // Ignore mutations to 'key' until OnRemoveItemComplete.
88 ignore_key_mutations_
[key
]++;
90 connection_id
, key
, page_url
,
91 base::Bind(&DOMStorageCachedArea::OnRemoveItemComplete
,
92 weak_factory_
.GetWeakPtr(), key
));
95 void DOMStorageCachedArea::Clear(int connection_id
, const GURL
& page_url
) {
96 // No need to prime the cache in this case.
98 map_
= new DOMStorageMap(kPerStorageAreaQuota
);
100 // Ignore all mutations until OnClearComplete time.
101 ignore_all_mutations_
= true;
102 proxy_
->ClearArea(connection_id
,
104 base::Bind(&DOMStorageCachedArea::OnClearComplete
,
105 weak_factory_
.GetWeakPtr()));
108 void DOMStorageCachedArea::ApplyMutation(
109 const base::NullableString16
& key
,
110 const base::NullableString16
& new_value
) {
111 if (!map_
.get() || ignore_all_mutations_
)
115 // It's a clear event.
116 scoped_refptr
<DOMStorageMap
> old
= map_
;
117 map_
= new DOMStorageMap(kPerStorageAreaQuota
);
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
);
134 // We have to retain local changes.
135 if (should_ignore_key_mutation(key
.string()))
138 if (new_value
.is_null()) {
139 // It's a remove item event.
140 base::string16 unused
;
141 map_
->RemoveItem(key
.string(), &unused
);
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
);
154 size_t DOMStorageCachedArea::MemoryBytesUsedByCache() const {
155 return map_
.get() ? map_
->bytes_used() : 0;
158 void DOMStorageCachedArea::Prime(int connection_id
) {
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
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
,
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",
182 map_
= new DOMStorageMap(kPerStorageAreaQuota
);
183 map_
->SwapValues(&values
);
184 if (send_log_get_messages
)
185 remaining_log_get_messages_
= kMaxLogGetMessagesToSend
;
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
,
194 if (local_storage_size_kb
< 100) {
196 "LocalStorage.RendererTimeToPrimeLocalStorageUnder100KB",
198 } else if (local_storage_size_kb
< 1000) {
200 "LocalStorage.RendererTimeToPrimeLocalStorage100KBTo1MB",
204 "LocalStorage.RendererTimeToPrimeLocalStorage1MBTo5MB",
209 void DOMStorageCachedArea::Reset() {
211 weak_factory_
.InvalidateWeakPtrs();
212 ignore_key_mutations_
.clear();
213 ignore_all_mutations_
= false;
216 void DOMStorageCachedArea::OnLoadComplete(bool success
) {
218 DCHECK(ignore_all_mutations_
);
219 ignore_all_mutations_
= false;
222 void DOMStorageCachedArea::OnSetItemComplete(const base::string16
& key
,
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
);
235 void DOMStorageCachedArea::OnRemoveItemComplete(const base::string16
& key
,
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
);
245 void DOMStorageCachedArea::OnClearComplete(bool success
) {
247 DCHECK(ignore_all_mutations_
);
248 ignore_all_mutations_
= false;
251 } // namespace content