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