OLD | NEW |
1 // Copyright (c) 2012 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 "webkit/dom_storage/dom_storage_area.h" | 5 #include "webkit/dom_storage/dom_storage_area.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/file_util.h" | |
9 #include "base/location.h" | 8 #include "base/location.h" |
10 #include "base/logging.h" | 9 #include "base/logging.h" |
11 #include "base/time.h" | 10 #include "base/time.h" |
12 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" | 11 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" |
13 #include "webkit/database/database_util.h" | 12 #include "webkit/database/database_util.h" |
14 #include "webkit/dom_storage/dom_storage_map.h" | 13 #include "webkit/dom_storage/dom_storage_map.h" |
15 #include "webkit/dom_storage/dom_storage_namespace.h" | 14 #include "webkit/dom_storage/dom_storage_namespace.h" |
16 #include "webkit/dom_storage/dom_storage_task_runner.h" | 15 #include "webkit/dom_storage/dom_storage_task_runner.h" |
17 #include "webkit/dom_storage/dom_storage_types.h" | 16 #include "webkit/dom_storage/dom_storage_types.h" |
| 17 #include "webkit/dom_storage/local_storage_database_adapter.h" |
| 18 #include "webkit/dom_storage/session_storage_database.h" |
| 19 #include "webkit/dom_storage/session_storage_database_adapter.h" |
18 #include "webkit/fileapi/file_system_util.h" | 20 #include "webkit/fileapi/file_system_util.h" |
19 #include "webkit/glue/webkit_glue.h" | 21 #include "webkit/glue/webkit_glue.h" |
20 | 22 |
21 using webkit_database::DatabaseUtil; | 23 using webkit_database::DatabaseUtil; |
22 | 24 |
23 namespace dom_storage { | 25 namespace dom_storage { |
24 | 26 |
25 static const int kCommitTimerSeconds = 1; | 27 static const int kCommitTimerSeconds = 1; |
26 | 28 |
27 DomStorageArea::CommitBatch::CommitBatch() | 29 DomStorageArea::CommitBatch::CommitBatch() |
(...skipping 27 matching lines...) Expand all Loading... |
55 DomStorageArea::DomStorageArea( | 57 DomStorageArea::DomStorageArea( |
56 int64 namespace_id, const GURL& origin, | 58 int64 namespace_id, const GURL& origin, |
57 const FilePath& directory, DomStorageTaskRunner* task_runner) | 59 const FilePath& directory, DomStorageTaskRunner* task_runner) |
58 : namespace_id_(namespace_id), origin_(origin), | 60 : namespace_id_(namespace_id), origin_(origin), |
59 directory_(directory), | 61 directory_(directory), |
60 task_runner_(task_runner), | 62 task_runner_(task_runner), |
61 map_(new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance)), | 63 map_(new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance)), |
62 is_initial_import_done_(true), | 64 is_initial_import_done_(true), |
63 is_shutdown_(false), | 65 is_shutdown_(false), |
64 commit_batches_in_flight_(0) { | 66 commit_batches_in_flight_(0) { |
65 if (namespace_id == kLocalStorageNamespaceId && !directory.empty()) { | 67 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id); |
| 68 if (!directory.empty()) { |
66 FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_)); | 69 FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_)); |
67 backing_.reset(new DomStorageDatabase(path)); | 70 backing_.reset(new LocalStorageDatabaseAdapter(path)); |
68 is_initial_import_done_ = false; | 71 is_initial_import_done_ = false; |
69 } | 72 } |
70 } | 73 } |
| 74 |
| 75 DomStorageArea::DomStorageArea( |
| 76 int64 namespace_id, |
| 77 const std::string& persistent_namespace_id, |
| 78 const GURL& origin, |
| 79 SessionStorageDatabase* session_storage_backing, |
| 80 DomStorageTaskRunner* task_runner) |
| 81 : namespace_id_(namespace_id), |
| 82 persistent_namespace_id_(persistent_namespace_id), |
| 83 origin_(origin), |
| 84 task_runner_(task_runner), |
| 85 map_(new DomStorageMap(kPerAreaQuota)), |
| 86 session_storage_backing_(session_storage_backing), |
| 87 is_initial_import_done_(true), |
| 88 is_shutdown_(false), |
| 89 commit_batches_in_flight_(0) { |
| 90 DCHECK(namespace_id != kLocalStorageNamespaceId); |
| 91 if (session_storage_backing) { |
| 92 backing_.reset(new SessionStorageDatabaseAdapter( |
| 93 session_storage_backing, persistent_namespace_id, origin)); |
| 94 is_initial_import_done_ = false; |
| 95 } |
| 96 } |
71 | 97 |
72 DomStorageArea::~DomStorageArea() { | 98 DomStorageArea::~DomStorageArea() { |
73 } | 99 } |
74 | 100 |
75 void DomStorageArea::ExtractValues(ValuesMap* map) { | 101 void DomStorageArea::ExtractValues(ValuesMap* map) { |
76 if (is_shutdown_) | 102 if (is_shutdown_) |
77 return; | 103 return; |
78 InitialImportIfNeeded(); | 104 InitialImportIfNeeded(); |
79 map_->ExtractValues(map); | 105 map_->ExtractValues(map); |
80 } | 106 } |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
141 | 167 |
142 if (backing_.get()) { | 168 if (backing_.get()) { |
143 CommitBatch* commit_batch = CreateCommitBatchIfNeeded(); | 169 CommitBatch* commit_batch = CreateCommitBatchIfNeeded(); |
144 commit_batch->clear_all_first = true; | 170 commit_batch->clear_all_first = true; |
145 commit_batch->changed_values.clear(); | 171 commit_batch->changed_values.clear(); |
146 } | 172 } |
147 | 173 |
148 return true; | 174 return true; |
149 } | 175 } |
150 | 176 |
151 DomStorageArea* DomStorageArea::ShallowCopy(int64 destination_namespace_id) { | 177 DomStorageArea* DomStorageArea::ShallowCopy( |
| 178 int64 destination_namespace_id, |
| 179 const std::string& destination_persistent_namespace_id) { |
152 DCHECK_NE(kLocalStorageNamespaceId, namespace_id_); | 180 DCHECK_NE(kLocalStorageNamespaceId, namespace_id_); |
153 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id); | 181 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id); |
154 DCHECK(!backing_.get()); // SessionNamespaces aren't stored on disk. | |
155 | 182 |
156 DomStorageArea* copy = new DomStorageArea(destination_namespace_id, origin_, | 183 DomStorageArea* copy = new DomStorageArea( |
157 FilePath(), task_runner_); | 184 destination_namespace_id, persistent_namespace_id_, origin_, |
| 185 session_storage_backing_, task_runner_); |
158 copy->map_ = map_; | 186 copy->map_ = map_; |
159 copy->is_shutdown_ = is_shutdown_; | 187 copy->is_shutdown_ = is_shutdown_; |
| 188 |
| 189 // All the uncommitted changes to this area need to happen before the actual |
| 190 // shallow copy is made (scheduled by the upper layer). Another OnCommitTimer |
| 191 // call might be in the event queue at this point, but it's handled gracefully |
| 192 // when it fires. |
| 193 if (commit_batch_.get()) |
| 194 OnCommitTimer(); |
160 return copy; | 195 return copy; |
161 } | 196 } |
162 | 197 |
163 bool DomStorageArea::HasUncommittedChanges() const { | 198 bool DomStorageArea::HasUncommittedChanges() const { |
164 DCHECK(!is_shutdown_); | 199 DCHECK(!is_shutdown_); |
165 return commit_batch_.get() || commit_batches_in_flight_; | 200 return commit_batch_.get() || commit_batches_in_flight_; |
166 } | 201 } |
167 | 202 |
168 void DomStorageArea::DeleteOrigin() { | 203 void DomStorageArea::DeleteOrigin() { |
169 DCHECK(!is_shutdown_); | 204 DCHECK(!is_shutdown_); |
170 if (HasUncommittedChanges()) { | 205 if (HasUncommittedChanges()) { |
171 // TODO(michaeln): This logically deletes the data immediately, | 206 // TODO(michaeln): This logically deletes the data immediately, |
172 // and in a matter of a second, deletes the rows from the backing | 207 // and in a matter of a second, deletes the rows from the backing |
173 // database file, but the file itself will linger until shutdown | 208 // database file, but the file itself will linger until shutdown |
174 // or purge time. Ideally, this should delete the file more | 209 // or purge time. Ideally, this should delete the file more |
175 // quickly. | 210 // quickly. |
176 Clear(); | 211 Clear(); |
177 return; | 212 return; |
178 } | 213 } |
179 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance); | 214 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance); |
180 if (backing_.get()) { | 215 if (backing_.get() && !session_storage_backing_.get()) { |
| 216 // This is localStorage. |
181 is_initial_import_done_ = false; | 217 is_initial_import_done_ = false; |
182 backing_.reset(new DomStorageDatabase(backing_->file_path())); | 218 backing_->Reset(); |
183 file_util::Delete(backing_->file_path(), false); | 219 backing_->DeleteFiles(); |
184 file_util::Delete( | 220 } else if (session_storage_backing_.get()) { |
185 DomStorageDatabase::GetJournalFilePath(backing_->file_path()), false); | 221 // No need to read the data (there will be no data), also, the |
| 222 // PRIMARY_SEQUENCE thread shouldn't try to read the data while |
| 223 // SessionStorageDatabase::DeleteArea is in progress. |
| 224 is_initial_import_done_ = true; |
| 225 task_runner_->PostShutdownBlockingTask( |
| 226 FROM_HERE, |
| 227 DomStorageTaskRunner::COMMIT_SEQUENCE, |
| 228 base::Bind(base::IgnoreResult(&SessionStorageDatabase::DeleteArea), |
| 229 session_storage_backing_.get(), |
| 230 persistent_namespace_id_, origin_)); |
186 } | 231 } |
187 } | 232 } |
188 | 233 |
189 void DomStorageArea::PurgeMemory() { | 234 void DomStorageArea::PurgeMemory() { |
190 DCHECK(!is_shutdown_); | 235 DCHECK(!is_shutdown_); |
191 if (!is_initial_import_done_ || // We're not using any memory. | 236 if (!is_initial_import_done_ || // We're not using any memory. |
192 !backing_.get() || // We can't purge anything. | 237 !backing_.get() || // We can't purge anything. |
193 HasUncommittedChanges()) // We leave things alone with changes pending. | 238 HasUncommittedChanges()) // We leave things alone with changes pending. |
194 return; | 239 return; |
195 | 240 |
196 // Drop the in memory cache, we'll reload when needed. | 241 // Drop the in memory cache, we'll reload when needed. |
197 is_initial_import_done_ = false; | 242 is_initial_import_done_ = false; |
198 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance); | 243 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance); |
199 | 244 |
200 // Recreate the database object, this frees up the open sqlite connection | 245 // Recreate the database object, this frees up the open sqlite connection |
201 // and its page cache. | 246 // and its page cache. |
202 backing_.reset(new DomStorageDatabase(backing_->file_path())); | 247 backing_->Reset(); |
203 } | 248 } |
204 | 249 |
205 void DomStorageArea::Shutdown() { | 250 void DomStorageArea::Shutdown() { |
206 DCHECK(!is_shutdown_); | 251 DCHECK(!is_shutdown_); |
207 is_shutdown_ = true; | 252 is_shutdown_ = true; |
208 map_ = NULL; | 253 map_ = NULL; |
209 if (!backing_.get()) | 254 if (!backing_.get()) |
210 return; | 255 return; |
211 | 256 |
212 bool success = task_runner_->PostShutdownBlockingTask( | 257 bool success = task_runner_->PostShutdownBlockingTask( |
213 FROM_HERE, | 258 FROM_HERE, |
214 DomStorageTaskRunner::COMMIT_SEQUENCE, | 259 DomStorageTaskRunner::COMMIT_SEQUENCE, |
215 base::Bind(&DomStorageArea::ShutdownInCommitSequence, this)); | 260 base::Bind(&DomStorageArea::ShutdownInCommitSequence, this)); |
216 DCHECK(success); | 261 DCHECK(success); |
217 } | 262 } |
218 | 263 |
219 void DomStorageArea::InitialImportIfNeeded() { | 264 void DomStorageArea::InitialImportIfNeeded() { |
220 if (is_initial_import_done_) | 265 if (is_initial_import_done_) |
221 return; | 266 return; |
222 | 267 |
223 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_); | |
224 DCHECK(backing_.get()); | 268 DCHECK(backing_.get()); |
225 | 269 |
226 ValuesMap initial_values; | 270 ValuesMap initial_values; |
227 backing_->ReadAllValues(&initial_values); | 271 backing_->ReadAllValues(&initial_values); |
228 map_->SwapValues(&initial_values); | 272 map_->SwapValues(&initial_values); |
229 is_initial_import_done_ = true; | 273 is_initial_import_done_ = true; |
230 } | 274 } |
231 | 275 |
232 DomStorageArea::CommitBatch* DomStorageArea::CreateCommitBatchIfNeeded() { | 276 DomStorageArea::CommitBatch* DomStorageArea::CreateCommitBatchIfNeeded() { |
233 DCHECK(!is_shutdown_); | 277 DCHECK(!is_shutdown_); |
234 if (!commit_batch_.get()) { | 278 if (!commit_batch_.get()) { |
235 commit_batch_.reset(new CommitBatch()); | 279 commit_batch_.reset(new CommitBatch()); |
236 | 280 |
237 // Start a timer to commit any changes that accrue in the batch, but only if | 281 // Start a timer to commit any changes that accrue in the batch, but only if |
238 // no commits are currently in flight. In that case the timer will be | 282 // no commits are currently in flight. In that case the timer will be |
239 // started after the commits have happened. | 283 // started after the commits have happened. |
240 if (!commit_batches_in_flight_) { | 284 if (!commit_batches_in_flight_) { |
241 task_runner_->PostDelayedTask( | 285 task_runner_->PostDelayedTask( |
242 FROM_HERE, | 286 FROM_HERE, |
243 base::Bind(&DomStorageArea::OnCommitTimer, this), | 287 base::Bind(&DomStorageArea::OnCommitTimer, this), |
244 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); | 288 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); |
245 } | 289 } |
246 } | 290 } |
247 return commit_batch_.get(); | 291 return commit_batch_.get(); |
248 } | 292 } |
249 | 293 |
250 void DomStorageArea::OnCommitTimer() { | 294 void DomStorageArea::OnCommitTimer() { |
251 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_); | |
252 if (is_shutdown_) | 295 if (is_shutdown_) |
253 return; | 296 return; |
254 | 297 |
255 DCHECK(backing_.get()); | 298 DCHECK(backing_.get()); |
256 DCHECK(commit_batch_.get()); | 299 |
257 DCHECK(!commit_batches_in_flight_); | 300 // It's possible that there is nothing to commit, since a shallow copy occured |
| 301 // before the timer fired. |
| 302 if (!commit_batch_.get()) |
| 303 return; |
258 | 304 |
259 // This method executes on the primary sequence, we schedule | 305 // This method executes on the primary sequence, we schedule |
260 // a task for immediate execution on the commit sequence. | 306 // a task for immediate execution on the commit sequence. |
261 DCHECK(task_runner_->IsRunningOnPrimarySequence()); | 307 DCHECK(task_runner_->IsRunningOnPrimarySequence()); |
262 bool success = task_runner_->PostShutdownBlockingTask( | 308 bool success = task_runner_->PostShutdownBlockingTask( |
263 FROM_HERE, | 309 FROM_HERE, |
264 DomStorageTaskRunner::COMMIT_SEQUENCE, | 310 DomStorageTaskRunner::COMMIT_SEQUENCE, |
265 base::Bind(&DomStorageArea::CommitChanges, this, | 311 base::Bind(&DomStorageArea::CommitChanges, this, |
266 base::Owned(commit_batch_.release()))); | 312 base::Owned(commit_batch_.release()))); |
267 ++commit_batches_in_flight_; | 313 ++commit_batches_in_flight_; |
268 DCHECK(success); | 314 DCHECK(success); |
269 } | 315 } |
270 | 316 |
271 void DomStorageArea::CommitChanges(const CommitBatch* commit_batch) { | 317 void DomStorageArea::CommitChanges(const CommitBatch* commit_batch) { |
272 // This method executes on the commit sequence. | 318 // This method executes on the commit sequence. |
273 DCHECK(task_runner_->IsRunningOnCommitSequence()); | 319 DCHECK(task_runner_->IsRunningOnCommitSequence()); |
274 bool success = backing_->CommitChanges(commit_batch->clear_all_first, | 320 bool success = backing_->CommitChanges(commit_batch->clear_all_first, |
275 commit_batch->changed_values); | 321 commit_batch->changed_values); |
276 DCHECK(success); // TODO(michaeln): what if it fails? | 322 DCHECK(success); // TODO(michaeln): what if it fails? |
277 task_runner_->PostTask( | 323 task_runner_->PostTask( |
278 FROM_HERE, | 324 FROM_HERE, |
279 base::Bind(&DomStorageArea::OnCommitComplete, this)); | 325 base::Bind(&DomStorageArea::OnCommitComplete, this)); |
280 } | 326 } |
281 | 327 |
282 void DomStorageArea::OnCommitComplete() { | 328 void DomStorageArea::OnCommitComplete() { |
283 // We're back on the primary sequence in this method. | 329 // We're back on the primary sequence in this method. |
284 DCHECK(task_runner_->IsRunningOnPrimarySequence()); | 330 DCHECK(task_runner_->IsRunningOnPrimarySequence()); |
| 331 --commit_batches_in_flight_; |
285 if (is_shutdown_) | 332 if (is_shutdown_) |
286 return; | 333 return; |
287 --commit_batches_in_flight_; | |
288 if (commit_batch_.get() && !commit_batches_in_flight_) { | 334 if (commit_batch_.get() && !commit_batches_in_flight_) { |
289 // More changes have accrued, restart the timer. | 335 // More changes have accrued, restart the timer. |
290 task_runner_->PostDelayedTask( | 336 task_runner_->PostDelayedTask( |
291 FROM_HERE, | 337 FROM_HERE, |
292 base::Bind(&DomStorageArea::OnCommitTimer, this), | 338 base::Bind(&DomStorageArea::OnCommitTimer, this), |
293 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); | 339 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); |
294 } | 340 } |
295 } | 341 } |
296 | 342 |
297 void DomStorageArea::ShutdownInCommitSequence() { | 343 void DomStorageArea::ShutdownInCommitSequence() { |
298 // This method executes on the commit sequence. | 344 // This method executes on the commit sequence. |
299 DCHECK(task_runner_->IsRunningOnCommitSequence()); | 345 DCHECK(task_runner_->IsRunningOnCommitSequence()); |
300 DCHECK(backing_.get()); | 346 DCHECK(backing_.get()); |
301 if (commit_batch_.get()) { | 347 if (commit_batch_.get()) { |
302 // Commit any changes that accrued prior to the timer firing. | 348 // Commit any changes that accrued prior to the timer firing. |
303 bool success = backing_->CommitChanges( | 349 bool success = backing_->CommitChanges( |
304 commit_batch_->clear_all_first, | 350 commit_batch_->clear_all_first, |
305 commit_batch_->changed_values); | 351 commit_batch_->changed_values); |
306 DCHECK(success); | 352 DCHECK(success); |
307 } | 353 } |
308 commit_batch_.reset(); | 354 commit_batch_.reset(); |
309 backing_.reset(); | 355 backing_.reset(); |
| 356 session_storage_backing_ = NULL; |
310 } | 357 } |
311 | 358 |
312 } // namespace dom_storage | 359 } // namespace dom_storage |
OLD | NEW |