OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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/browser/in_process_webkit/dom_storage_context_impl.h" | |
6 | |
7 #ifdef ENABLE_NEW_DOM_STORAGE_BACKEND | |
8 // This class is replaced by a new implementation in | |
9 // content/browser/dom_storage/dom_storage_context_impl_new.h | |
10 #else | |
11 | |
12 #include <algorithm> | |
13 | |
14 #include "base/bind.h" | |
15 #include "base/file_path.h" | |
16 #include "base/file_util.h" | |
17 #include "base/string_util.h" | |
18 #include "content/browser/in_process_webkit/dom_storage_area.h" | |
19 #include "content/browser/in_process_webkit/dom_storage_namespace.h" | |
20 #include "content/common/dom_storage_common.h" | |
21 #include "content/public/browser/browser_thread.h" | |
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" | |
23 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" | |
24 #include "webkit/glue/webkit_glue.h" | |
25 #include "webkit/quota/special_storage_policy.h" | |
26 | |
27 using content::BrowserThread; | |
28 using content::DOMStorageContext; | |
29 using WebKit::WebSecurityOrigin; | |
30 | |
31 const FilePath::CharType DOMStorageContextImpl::kLocalStorageDirectory[] = | |
32 FILE_PATH_LITERAL("Local Storage"); | |
33 | |
34 const FilePath::CharType DOMStorageContextImpl::kLocalStorageExtension[] = | |
35 FILE_PATH_LITERAL(".localstorage"); | |
36 | |
37 namespace { | |
38 | |
39 void ClearLocalState(const FilePath& domstorage_path, | |
40 quota::SpecialStoragePolicy* special_storage_policy, | |
41 bool clear_all_databases) { | |
42 file_util::FileEnumerator file_enumerator( | |
43 domstorage_path, false, file_util::FileEnumerator::FILES); | |
44 for (FilePath file_path = file_enumerator.Next(); !file_path.empty(); | |
45 file_path = file_enumerator.Next()) { | |
46 if (file_path.Extension() == | |
47 DOMStorageContextImpl::kLocalStorageExtension) { | |
48 GURL origin(WebSecurityOrigin::createFromDatabaseIdentifier( | |
49 webkit_glue::FilePathToWebString(file_path.BaseName())).toString()); | |
50 if (special_storage_policy && | |
51 special_storage_policy->IsStorageProtected(origin)) | |
52 continue; | |
53 if (!clear_all_databases && | |
54 !special_storage_policy->IsStorageSessionOnly(origin)) { | |
55 continue; | |
56 } | |
57 file_util::Delete(file_path, false); | |
58 } | |
59 } | |
60 } | |
61 | |
62 } // namespace | |
63 | |
64 DOMStorageContextImpl::DOMStorageContextImpl( | |
65 const FilePath& data_path, | |
66 quota::SpecialStoragePolicy* special_storage_policy) | |
67 : last_storage_area_id_(0), | |
68 last_session_storage_namespace_id_on_ui_thread_(kLocalStorageNamespaceId), | |
69 last_session_storage_namespace_id_on_io_thread_(kLocalStorageNamespaceId), | |
70 clear_local_state_on_exit_(false), | |
71 save_session_state_(false), | |
72 special_storage_policy_(special_storage_policy), | |
73 webkit_message_loop_( | |
74 BrowserThread::GetMessageLoopProxyForThread( | |
75 BrowserThread::WEBKIT_DEPRECATED)) { | |
76 data_path_ = data_path; | |
77 } | |
78 | |
79 DOMStorageContextImpl::~DOMStorageContextImpl() { | |
80 // This should not go away until all DOM Storage message filters have gone | |
81 // away. And they remove themselves from this list. | |
82 DCHECK(message_filter_set_.empty()); | |
83 | |
84 for (StorageNamespaceMap::iterator iter(storage_namespace_map_.begin()); | |
85 iter != storage_namespace_map_.end(); ++iter) { | |
86 delete iter->second; | |
87 } | |
88 | |
89 if (save_session_state_) | |
90 return; | |
91 | |
92 bool has_session_only_databases = | |
93 special_storage_policy_.get() && | |
94 special_storage_policy_->HasSessionOnlyOrigins(); | |
95 | |
96 // Clearning only session-only databases, and there are none. | |
97 if (!clear_local_state_on_exit_ && !has_session_only_databases) | |
98 return; | |
99 | |
100 // Not being on the WEBKIT thread here means we are running in a unit test | |
101 // where no clean up is needed. | |
102 if (BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)) { | |
103 ClearLocalState(data_path_.Append(kLocalStorageDirectory), | |
104 special_storage_policy_, | |
105 clear_local_state_on_exit_); | |
106 } | |
107 } | |
108 | |
109 int64 DOMStorageContextImpl::AllocateStorageAreaId() { | |
110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | |
111 return ++last_storage_area_id_; | |
112 } | |
113 | |
114 int64 DOMStorageContextImpl::AllocateSessionStorageNamespaceId() { | |
115 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) | |
116 return ++last_session_storage_namespace_id_on_ui_thread_; | |
117 return --last_session_storage_namespace_id_on_io_thread_; | |
118 } | |
119 | |
120 int64 DOMStorageContextImpl::CloneSessionStorage(int64 original_id) { | |
121 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | |
122 int64 clone_id = AllocateSessionStorageNamespaceId(); | |
123 BrowserThread::PostTask( | |
124 BrowserThread::WEBKIT_DEPRECATED, FROM_HERE, | |
125 base::Bind(&DOMStorageContextImpl::CompleteCloningSessionStorage, | |
126 this, original_id, clone_id)); | |
127 return clone_id; | |
128 } | |
129 | |
130 void DOMStorageContextImpl::RegisterStorageArea(DOMStorageArea* storage_area) { | |
131 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | |
132 int64 id = storage_area->id(); | |
133 DCHECK(!GetStorageArea(id)); | |
134 storage_area_map_[id] = storage_area; | |
135 } | |
136 | |
137 void DOMStorageContextImpl::UnregisterStorageArea( | |
138 DOMStorageArea* storage_area) { | |
139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | |
140 int64 id = storage_area->id(); | |
141 DCHECK(GetStorageArea(id)); | |
142 storage_area_map_.erase(id); | |
143 } | |
144 | |
145 DOMStorageArea* DOMStorageContextImpl::GetStorageArea(int64 id) { | |
146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | |
147 StorageAreaMap::iterator iter = storage_area_map_.find(id); | |
148 if (iter == storage_area_map_.end()) | |
149 return NULL; | |
150 return iter->second; | |
151 } | |
152 | |
153 void DOMStorageContextImpl::DeleteSessionStorageNamespace(int64 namespace_id) { | |
154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED) || | |
155 !BrowserThread::IsMessageLoopValid(BrowserThread::WEBKIT_DEPRECATED)); | |
156 StorageNamespaceMap::iterator iter = | |
157 storage_namespace_map_.find(namespace_id); | |
158 if (iter == storage_namespace_map_.end()) | |
159 return; | |
160 DCHECK(iter->second->dom_storage_type() == DOM_STORAGE_SESSION); | |
161 delete iter->second; | |
162 storage_namespace_map_.erase(iter); | |
163 } | |
164 | |
165 DOMStorageNamespace* DOMStorageContextImpl::GetStorageNamespace( | |
166 int64 id, bool allocation_allowed) { | |
167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | |
168 StorageNamespaceMap::iterator iter = storage_namespace_map_.find(id); | |
169 if (iter != storage_namespace_map_.end()) | |
170 return iter->second; | |
171 if (!allocation_allowed) | |
172 return NULL; | |
173 if (id == kLocalStorageNamespaceId) | |
174 return CreateLocalStorage(); | |
175 return CreateSessionStorage(id); | |
176 } | |
177 | |
178 void DOMStorageContextImpl::RegisterMessageFilter( | |
179 DOMStorageMessageFilter* message_filter) { | |
180 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
181 DCHECK(message_filter_set_.find(message_filter) == | |
182 message_filter_set_.end()); | |
183 message_filter_set_.insert(message_filter); | |
184 } | |
185 | |
186 void DOMStorageContextImpl::UnregisterMessageFilter( | |
187 DOMStorageMessageFilter* message_filter) { | |
188 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
189 DCHECK(message_filter_set_.find(message_filter) != | |
190 message_filter_set_.end()); | |
191 message_filter_set_.erase(message_filter); | |
192 } | |
193 | |
194 const DOMStorageContextImpl::MessageFilterSet* | |
195 DOMStorageContextImpl::GetMessageFilterSet() const { | |
196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
197 return &message_filter_set_; | |
198 } | |
199 | |
200 void DOMStorageContextImpl::PurgeMemory() { | |
201 if (!webkit_message_loop_->RunsTasksOnCurrentThread()) { | |
202 webkit_message_loop_->PostTask( | |
203 FROM_HERE, | |
204 base::Bind(&DOMStorageContextImpl::PurgeMemory, this)); | |
205 return; | |
206 } | |
207 | |
208 // It is only safe to purge the memory from the LocalStorage namespace, | |
209 // because it is backed by disk and can be reloaded later. If we purge a | |
210 // SessionStorage namespace, its data will be gone forever, because it isn't | |
211 // currently backed by disk. | |
212 DOMStorageNamespace* local_storage = | |
213 GetStorageNamespace(kLocalStorageNamespaceId, false); | |
214 if (local_storage) | |
215 local_storage->PurgeMemory(); | |
216 } | |
217 | |
218 void DOMStorageContextImpl::DeleteDataModifiedSince(const base::Time& cutoff) { | |
219 if (!webkit_message_loop_->RunsTasksOnCurrentThread()) { | |
220 webkit_message_loop_->PostTask( | |
221 FROM_HERE, | |
222 base::Bind( | |
223 &DOMStorageContextImpl::DeleteDataModifiedSince, this, cutoff)); | |
224 return; | |
225 } | |
226 | |
227 // Make sure that we don't delete a database that's currently being accessed | |
228 // by unloading all of the databases temporarily. | |
229 PurgeMemory(); | |
230 | |
231 file_util::FileEnumerator file_enumerator( | |
232 data_path_.Append(kLocalStorageDirectory), false, | |
233 file_util::FileEnumerator::FILES); | |
234 for (FilePath path = file_enumerator.Next(); !path.value().empty(); | |
235 path = file_enumerator.Next()) { | |
236 GURL origin(WebSecurityOrigin::createFromDatabaseIdentifier( | |
237 webkit_glue::FilePathToWebString(path.BaseName())).toString()); | |
238 if (special_storage_policy_->IsStorageProtected(origin)) | |
239 continue; | |
240 | |
241 file_util::FileEnumerator::FindInfo find_info; | |
242 file_enumerator.GetFindInfo(&find_info); | |
243 if (file_util::HasFileBeenModifiedSince(find_info, cutoff)) | |
244 file_util::Delete(path, false); | |
245 } | |
246 } | |
247 | |
248 void DOMStorageContextImpl::DeleteLocalStorageFile(const FilePath& file_path) { | |
249 if (!webkit_message_loop_->RunsTasksOnCurrentThread()) { | |
250 webkit_message_loop_->PostTask( | |
251 FROM_HERE, | |
252 base::Bind( | |
253 &DOMStorageContextImpl::DeleteLocalStorageFile, this, file_path)); | |
254 return; | |
255 } | |
256 | |
257 // Make sure that we don't delete a database that's currently being accessed | |
258 // by unloading all of the databases temporarily. | |
259 // TODO(bulach): both this method and DeleteDataModifiedSince could purge | |
260 // only the memory used by the specific file instead of all memory at once. | |
261 // See http://crbug.com/32000 | |
262 PurgeMemory(); | |
263 file_util::Delete(file_path, false); | |
264 } | |
265 | |
266 void DOMStorageContextImpl::DeleteForOrigin(const string16& origin_id) { | |
267 DeleteLocalStorageFile(GetFilePath(origin_id)); | |
268 } | |
269 | |
270 void DOMStorageContextImpl::DeleteAllLocalStorageFiles() { | |
271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | |
272 | |
273 // Make sure that we don't delete a database that's currently being accessed | |
274 // by unloading all of the databases temporarily. | |
275 PurgeMemory(); | |
276 | |
277 file_util::FileEnumerator file_enumerator( | |
278 data_path_.Append(kLocalStorageDirectory), false, | |
279 file_util::FileEnumerator::FILES); | |
280 for (FilePath file_path = file_enumerator.Next(); !file_path.empty(); | |
281 file_path = file_enumerator.Next()) { | |
282 if (file_path.Extension() == kLocalStorageExtension) | |
283 file_util::Delete(file_path, false); | |
284 } | |
285 } | |
286 | |
287 void DOMStorageContextImpl::SetClearLocalState(bool clear_local_state) { | |
288 if (!webkit_message_loop_->RunsTasksOnCurrentThread()) { | |
289 webkit_message_loop_->PostTask( | |
290 FROM_HERE, | |
291 base::Bind( | |
292 &DOMStorageContextImpl::SetClearLocalState, | |
293 this, clear_local_state)); | |
294 return; | |
295 } | |
296 clear_local_state_on_exit_ = clear_local_state; | |
297 } | |
298 | |
299 void DOMStorageContextImpl::SaveSessionState() { | |
300 if (!webkit_message_loop_->RunsTasksOnCurrentThread()) { | |
301 webkit_message_loop_->PostTask( | |
302 FROM_HERE, | |
303 base::Bind(&DOMStorageContextImpl::SaveSessionState, this)); | |
304 return; | |
305 } | |
306 save_session_state_ = true; | |
307 } | |
308 | |
309 DOMStorageNamespace* DOMStorageContextImpl::CreateLocalStorage() { | |
310 FilePath dir_path; | |
311 if (!data_path_.empty()) | |
312 dir_path = data_path_.Append(kLocalStorageDirectory); | |
313 DOMStorageNamespace* new_namespace = | |
314 DOMStorageNamespace::CreateLocalStorageNamespace(this, dir_path); | |
315 RegisterStorageNamespace(new_namespace); | |
316 return new_namespace; | |
317 } | |
318 | |
319 DOMStorageNamespace* DOMStorageContextImpl::CreateSessionStorage( | |
320 int64 namespace_id) { | |
321 DOMStorageNamespace* new_namespace = | |
322 DOMStorageNamespace::CreateSessionStorageNamespace(this, namespace_id); | |
323 RegisterStorageNamespace(new_namespace); | |
324 return new_namespace; | |
325 } | |
326 | |
327 void DOMStorageContextImpl::RegisterStorageNamespace( | |
328 DOMStorageNamespace* storage_namespace) { | |
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | |
330 int64 id = storage_namespace->id(); | |
331 DCHECK(!GetStorageNamespace(id, false)); | |
332 storage_namespace_map_[id] = storage_namespace; | |
333 } | |
334 | |
335 void DOMStorageContextImpl::CompleteCloningSessionStorage( | |
336 int64 existing_id, int64 clone_id) { | |
337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED)); | |
338 DOMStorageNamespace* existing_namespace = | |
339 GetStorageNamespace(existing_id, false); | |
340 // If nothing exists, then there's nothing to clone. | |
341 if (existing_namespace) | |
342 RegisterStorageNamespace(existing_namespace->Copy(clone_id)); | |
343 } | |
344 | |
345 void DOMStorageContextImpl::GetAllStorageFiles( | |
346 const GetAllStorageFilesCallback& callback) { | |
347 if (!webkit_message_loop_->RunsTasksOnCurrentThread()) { | |
348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
349 webkit_message_loop_->PostTask( | |
350 FROM_HERE, | |
351 base::Bind( | |
352 &DOMStorageContextImpl::GetAllStorageFiles, this, callback)); | |
353 return; | |
354 } | |
355 | |
356 std::vector<FilePath> files; | |
357 file_util::FileEnumerator file_enumerator( | |
358 data_path_.Append(kLocalStorageDirectory), false, | |
359 file_util::FileEnumerator::FILES); | |
360 for (FilePath file_path = file_enumerator.Next(); !file_path.empty(); | |
361 file_path = file_enumerator.Next()) { | |
362 if (file_path.Extension() == kLocalStorageExtension) | |
363 files.push_back(file_path); | |
364 } | |
365 | |
366 BrowserThread::PostTask( | |
367 BrowserThread::UI, FROM_HERE, | |
368 base::Bind(&DOMStorageContextImpl::RunAllStorageFilesCallback, | |
369 this, files, callback)); | |
370 } | |
371 | |
372 void DOMStorageContextImpl::RunAllStorageFilesCallback( | |
373 const std::vector<FilePath>& files, | |
374 const GetAllStorageFilesCallback& callback) { | |
375 callback.Run(files); | |
376 } | |
377 | |
378 FilePath DOMStorageContextImpl::GetFilePath(const string16& origin_id) const { | |
379 FilePath storage_dir = data_path_.Append(kLocalStorageDirectory); | |
380 FilePath::StringType id = webkit_glue::WebStringToFilePathString(origin_id); | |
381 return storage_dir.Append(id.append(kLocalStorageExtension)); | |
382 } | |
383 | |
384 #endif // ENABLE_NEW_DOM_STORAGE_BACKEND | |
OLD | NEW |