OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 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/browser/in_process_webkit/dom_storage_context.h" | 5 #include "webkit/dom_storage/dom_storage_context.h" |
6 | |
7 #include <algorithm> | |
8 | 6 |
9 #include "base/bind.h" | 7 #include "base/bind.h" |
10 #include "base/file_path.h" | 8 #include "base/bind_helpers.h" |
11 #include "base/file_util.h" | 9 #include "base/time.h" |
12 #include "base/string_util.h" | 10 #include "webkit/dom_storage/dom_storage_task_runner.h" |
13 #include "content/browser/in_process_webkit/dom_storage_area.h" | |
14 #include "content/browser/in_process_webkit/dom_storage_namespace.h" | |
15 #include "content/browser/in_process_webkit/webkit_context.h" | |
16 #include "content/common/dom_storage_common.h" | |
17 #include "content/public/browser/browser_thread.h" | |
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" | |
19 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" | |
20 #include "webkit/glue/webkit_glue.h" | |
21 #include "webkit/quota/special_storage_policy.h" | 11 #include "webkit/quota/special_storage_policy.h" |
22 | 12 |
23 using content::BrowserThread; | 13 namespace dom_storage { |
24 | 14 |
25 using WebKit::WebSecurityOrigin; | 15 DomStorageContext::DomStorageContext( |
26 | 16 const FilePath& directory, |
27 namespace { | 17 quota::SpecialStoragePolicy* special_storage_policy, |
28 | 18 DomStorageTaskRunner* task_runner) |
29 void ClearLocalState(const FilePath& domstorage_path, | 19 : directory_(directory), |
30 quota::SpecialStoragePolicy* special_storage_policy, | 20 special_storage_policy_(special_storage_policy), |
31 bool clear_all_databases) { | 21 task_runner_(task_runner), |
32 file_util::FileEnumerator file_enumerator( | 22 clear_local_state_on_exit_(false), |
33 domstorage_path, false, file_util::FileEnumerator::FILES); | 23 save_session_state_(false) { |
34 for (FilePath file_path = file_enumerator.Next(); !file_path.empty(); | |
35 file_path = file_enumerator.Next()) { | |
36 if (file_path.Extension() == DOMStorageContext::kLocalStorageExtension) { | |
37 GURL origin(WebSecurityOrigin::createFromDatabaseIdentifier( | |
38 webkit_glue::FilePathToWebString(file_path.BaseName())).toString()); | |
39 if (special_storage_policy->IsStorageProtected(origin)) | |
40 continue; | |
41 if (!clear_all_databases && | |
42 !special_storage_policy->IsStorageSessionOnly(origin)) { | |
43 continue; | |
44 } | |
45 file_util::Delete(file_path, false); | |
46 } | |
47 } | |
48 } | 24 } |
49 | 25 |
50 } // namespace | 26 DomStorageContext::~DomStorageContext() { |
51 | |
52 const FilePath::CharType DOMStorageContext::kLocalStorageDirectory[] = | |
53 FILE_PATH_LITERAL("Local Storage"); | |
54 | |
55 const FilePath::CharType DOMStorageContext::kLocalStorageExtension[] = | |
56 FILE_PATH_LITERAL(".localstorage"); | |
57 | |
58 DOMStorageContext::DOMStorageContext( | |
59 WebKitContext* webkit_context, | |
60 quota::SpecialStoragePolicy* special_storage_policy) | |
61 : last_storage_area_id_(0), | |
62 last_session_storage_namespace_id_on_ui_thread_(kLocalStorageNamespaceId), | |
63 last_session_storage_namespace_id_on_io_thread_(kLocalStorageNamespaceId), | |
64 clear_local_state_on_exit_(false), | |
65 save_session_state_(false), | |
66 special_storage_policy_(special_storage_policy) { | |
67 data_path_ = webkit_context->data_path(); | |
68 } | 27 } |
69 | 28 |
70 DOMStorageContext::~DOMStorageContext() { | 29 void DomStorageContext::AddEventObserver(EventObserver* observer) { |
71 // This should not go away until all DOM Storage message filters have gone | 30 event_observers_.AddObserver(observer); |
72 // away. And they remove themselves from this list. | |
73 DCHECK(message_filter_set_.empty()); | |
74 | |
75 for (StorageNamespaceMap::iterator iter(storage_namespace_map_.begin()); | |
76 iter != storage_namespace_map_.end(); ++iter) { | |
77 delete iter->second; | |
78 } | |
79 | |
80 if (save_session_state_) | |
81 return; | |
82 | |
83 bool has_session_only_databases = | |
84 special_storage_policy_.get() && | |
85 special_storage_policy_->HasSessionOnlyOrigins(); | |
86 | |
87 // Clearning only session-only databases, and there are none. | |
88 if (!clear_local_state_on_exit_ && !has_session_only_databases) | |
89 return; | |
90 | |
91 // Not being on the WEBKIT thread here means we are running in a unit test | |
92 // where no clean up is needed. | |
93 if (BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)) { | |
94 ClearLocalState(data_path_.Append(kLocalStorageDirectory), | |
95 special_storage_policy_, | |
96 clear_local_state_on_exit_); | |
97 } | |
98 } | 31 } |
99 | 32 |
100 int64 DOMStorageContext::AllocateStorageAreaId() { | 33 void DomStorageContext::RemoveEventObserver(EventObserver* observer) { |
101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | 34 event_observers_.RemoveObserver(observer); |
102 return ++last_storage_area_id_; | |
103 } | 35 } |
104 | 36 |
105 int64 DOMStorageContext::AllocateSessionStorageNamespaceId() { | 37 void DomStorageContext::NotifyItemSet( |
106 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) | 38 DomStorageArea* area, |
107 return ++last_session_storage_namespace_id_on_ui_thread_; | 39 const string16& key, |
108 return --last_session_storage_namespace_id_on_io_thread_; | 40 const string16& new_value, |
| 41 const NullableString16& old_value, |
| 42 const GURL& page_url) { |
| 43 FOR_EACH_OBSERVER( |
| 44 EventObserver, event_observers_, |
| 45 OnDomStorageItemSet(area, key, new_value, old_value, page_url)); |
109 } | 46 } |
110 | 47 |
111 int64 DOMStorageContext::CloneSessionStorage(int64 original_id) { | 48 void DomStorageContext::NotifyItemRemoved( |
112 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | 49 DomStorageArea* area, |
113 int64 clone_id = AllocateSessionStorageNamespaceId(); | 50 const string16& key, |
114 BrowserThread::PostTask( | 51 const string16& old_value, |
115 BrowserThread::WEBKIT_DEPRECATED, FROM_HERE, | 52 const GURL& page_url) { |
116 base::Bind(&DOMStorageContext::CompleteCloningSessionStorage, this, | 53 FOR_EACH_OBSERVER( |
117 original_id, clone_id)); | 54 EventObserver, event_observers_, |
118 return clone_id; | 55 OnDomStorageItemRemoved(area, key, old_value, page_url)); |
119 } | 56 } |
120 | 57 |
121 void DOMStorageContext::RegisterStorageArea(DOMStorageArea* storage_area) { | 58 void DomStorageContext::NotifyAreaCleared( |
122 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | 59 DomStorageArea* area, |
123 int64 id = storage_area->id(); | 60 const GURL& page_url) { |
124 DCHECK(!GetStorageArea(id)); | 61 FOR_EACH_OBSERVER( |
125 storage_area_map_[id] = storage_area; | 62 EventObserver, event_observers_, |
| 63 OnDomStorageAreaCleared(area, page_url)); |
126 } | 64 } |
127 | 65 |
128 void DOMStorageContext::UnregisterStorageArea(DOMStorageArea* storage_area) { | 66 DomStorageNamespace* DomStorageContext::GetStorageNamespace(int64 namespace_id)
{ |
129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | 67 return NULL; |
130 int64 id = storage_area->id(); | |
131 DCHECK(GetStorageArea(id)); | |
132 storage_area_map_.erase(id); | |
133 } | 68 } |
134 | 69 |
135 DOMStorageArea* DOMStorageContext::GetStorageArea(int64 id) { | 70 int64 DomStorageContext::CreateSessionStorageNamespace() { |
136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | 71 int64 new_id = session_storage_id_sequence_.GetNext() + 1; |
137 StorageAreaMap::iterator iter = storage_area_map_.find(id); | 72 task_runner_->PostTask( |
138 if (iter == storage_area_map_.end()) | 73 FROM_HERE, |
139 return NULL; | 74 base::Bind(&DomStorageContext::CreateSessionStorageNamespaceHelper, |
140 return iter->second; | 75 this, new_id)); |
| 76 return new_id; |
141 } | 77 } |
142 | 78 |
143 void DOMStorageContext::DeleteSessionStorageNamespace(int64 namespace_id) { | 79 void DomStorageContext::DeleteSessionStorageNamespace(int64 namespace_id) { |
144 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | 80 task_runner_->PostTask( |
145 StorageNamespaceMap::iterator iter = | 81 FROM_HERE, |
146 storage_namespace_map_.find(namespace_id); | 82 base::Bind(&DomStorageContext::DeleteSessionStorageNamespaceHelper, |
147 if (iter == storage_namespace_map_.end()) | 83 this, namespace_id)); |
148 return; | |
149 DCHECK(iter->second->dom_storage_type() == DOM_STORAGE_SESSION); | |
150 delete iter->second; | |
151 storage_namespace_map_.erase(iter); | |
152 } | 84 } |
153 | 85 |
154 DOMStorageNamespace* DOMStorageContext::GetStorageNamespace( | 86 int64 DomStorageContext::CloneSessionStorageNamespace(int64 existing_id) { |
155 int64 id, bool allocation_allowed) { | 87 int64 new_id = session_storage_id_sequence_.GetNext() + 1; |
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | 88 task_runner_->PostTask( |
157 StorageNamespaceMap::iterator iter = storage_namespace_map_.find(id); | 89 FROM_HERE, |
158 if (iter != storage_namespace_map_.end()) | 90 base::Bind(&DomStorageContext::CloneSessionStorageNamespaceHelper, |
159 return iter->second; | 91 this, existing_id, new_id)); |
160 if (!allocation_allowed) | 92 return new_id; |
161 return NULL; | |
162 if (id == kLocalStorageNamespaceId) | |
163 return CreateLocalStorage(); | |
164 return CreateSessionStorage(id); | |
165 } | 93 } |
166 | 94 |
167 void DOMStorageContext::RegisterMessageFilter( | 95 void DomStorageContext::CreateSessionStorageNamespaceHelper( |
168 DOMStorageMessageFilter* message_filter) { | 96 int64 new_id) { |
169 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
170 DCHECK(message_filter_set_.find(message_filter) == | |
171 message_filter_set_.end()); | |
172 message_filter_set_.insert(message_filter); | |
173 } | 97 } |
174 | 98 |
175 void DOMStorageContext::UnregisterMessageFilter( | 99 void DomStorageContext::DeleteSessionStorageNamespaceHelper( |
176 DOMStorageMessageFilter* message_filter) { | 100 int64 namespace_id) { |
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
178 DCHECK(message_filter_set_.find(message_filter) != | |
179 message_filter_set_.end()); | |
180 message_filter_set_.erase(message_filter); | |
181 } | 101 } |
182 | 102 |
183 const DOMStorageContext::MessageFilterSet* | 103 void DomStorageContext::CloneSessionStorageNamespaceHelper( |
184 DOMStorageContext::GetMessageFilterSet() const { | 104 int64 existing_id, int64 new_id) { |
185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
186 return &message_filter_set_; | |
187 } | 105 } |
188 | 106 |
189 void DOMStorageContext::PurgeMemory() { | 107 } // namespace dom_storage |
190 // It is only safe to purge the memory from the LocalStorage namespace, | |
191 // because it is backed by disk and can be reloaded later. If we purge a | |
192 // SessionStorage namespace, its data will be gone forever, because it isn't | |
193 // currently backed by disk. | |
194 DOMStorageNamespace* local_storage = | |
195 GetStorageNamespace(kLocalStorageNamespaceId, false); | |
196 if (local_storage) | |
197 local_storage->PurgeMemory(); | |
198 } | |
199 | 108 |
200 void DOMStorageContext::DeleteDataModifiedSince(const base::Time& cutoff) { | |
201 // Make sure that we don't delete a database that's currently being accessed | |
202 // by unloading all of the databases temporarily. | |
203 PurgeMemory(); | |
204 | |
205 file_util::FileEnumerator file_enumerator( | |
206 data_path_.Append(kLocalStorageDirectory), false, | |
207 file_util::FileEnumerator::FILES); | |
208 for (FilePath path = file_enumerator.Next(); !path.value().empty(); | |
209 path = file_enumerator.Next()) { | |
210 GURL origin(WebSecurityOrigin::createFromDatabaseIdentifier( | |
211 webkit_glue::FilePathToWebString(path.BaseName())).toString()); | |
212 if (special_storage_policy_->IsStorageProtected(origin)) | |
213 continue; | |
214 | |
215 file_util::FileEnumerator::FindInfo find_info; | |
216 file_enumerator.GetFindInfo(&find_info); | |
217 if (file_util::HasFileBeenModifiedSince(find_info, cutoff)) | |
218 file_util::Delete(path, false); | |
219 } | |
220 } | |
221 | |
222 void DOMStorageContext::DeleteLocalStorageFile(const FilePath& file_path) { | |
223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | |
224 | |
225 // Make sure that we don't delete a database that's currently being accessed | |
226 // by unloading all of the databases temporarily. | |
227 // TODO(bulach): both this method and DeleteDataModifiedSince could purge | |
228 // only the memory used by the specific file instead of all memory at once. | |
229 // See http://crbug.com/32000 | |
230 PurgeMemory(); | |
231 file_util::Delete(file_path, false); | |
232 } | |
233 | |
234 void DOMStorageContext::DeleteLocalStorageForOrigin(const string16& origin_id) { | |
235 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | |
236 DeleteLocalStorageFile(GetLocalStorageFilePath(origin_id)); | |
237 } | |
238 | |
239 void DOMStorageContext::DeleteAllLocalStorageFiles() { | |
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | |
241 | |
242 // Make sure that we don't delete a database that's currently being accessed | |
243 // by unloading all of the databases temporarily. | |
244 PurgeMemory(); | |
245 | |
246 file_util::FileEnumerator file_enumerator( | |
247 data_path_.Append(kLocalStorageDirectory), false, | |
248 file_util::FileEnumerator::FILES); | |
249 for (FilePath file_path = file_enumerator.Next(); !file_path.empty(); | |
250 file_path = file_enumerator.Next()) { | |
251 if (file_path.Extension() == kLocalStorageExtension) | |
252 file_util::Delete(file_path, false); | |
253 } | |
254 } | |
255 | |
256 DOMStorageNamespace* DOMStorageContext::CreateLocalStorage() { | |
257 FilePath dir_path; | |
258 if (!data_path_.empty()) | |
259 dir_path = data_path_.Append(kLocalStorageDirectory); | |
260 DOMStorageNamespace* new_namespace = | |
261 DOMStorageNamespace::CreateLocalStorageNamespace(this, dir_path); | |
262 RegisterStorageNamespace(new_namespace); | |
263 return new_namespace; | |
264 } | |
265 | |
266 DOMStorageNamespace* DOMStorageContext::CreateSessionStorage( | |
267 int64 namespace_id) { | |
268 DOMStorageNamespace* new_namespace = | |
269 DOMStorageNamespace::CreateSessionStorageNamespace(this, namespace_id); | |
270 RegisterStorageNamespace(new_namespace); | |
271 return new_namespace; | |
272 } | |
273 | |
274 void DOMStorageContext::RegisterStorageNamespace( | |
275 DOMStorageNamespace* storage_namespace) { | |
276 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | |
277 int64 id = storage_namespace->id(); | |
278 DCHECK(!GetStorageNamespace(id, false)); | |
279 storage_namespace_map_[id] = storage_namespace; | |
280 } | |
281 | |
282 /* static */ | |
283 void DOMStorageContext::CompleteCloningSessionStorage( | |
284 DOMStorageContext* context, int64 existing_id, int64 clone_id) { | |
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | |
286 DOMStorageNamespace* existing_namespace = | |
287 context->GetStorageNamespace(existing_id, false); | |
288 // If nothing exists, then there's nothing to clone. | |
289 if (existing_namespace) | |
290 context->RegisterStorageNamespace(existing_namespace->Copy(clone_id)); | |
291 } | |
292 | |
293 FilePath DOMStorageContext::GetLocalStorageFilePath( | |
294 const string16& origin_id) const { | |
295 FilePath storageDir = data_path_.Append( | |
296 DOMStorageContext::kLocalStorageDirectory); | |
297 FilePath::StringType id = | |
298 webkit_glue::WebStringToFilePathString(origin_id); | |
299 return storageDir.Append(id.append(kLocalStorageExtension)); | |
300 } | |
OLD | NEW |