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