Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(50)

Side by Side Diff: webkit/dom_storage/dom_storage_area.cc

Issue 9963107: Persist sessionStorage on disk. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebased. Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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/location.h" 8 #include "base/location.h"
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/time.h" 10 #include "base/time.h"
11 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" 11 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
12 #include "webkit/database/database_util.h" 12 #include "webkit/database/database_util.h"
13 #include "webkit/dom_storage/dom_storage_map.h" 13 #include "webkit/dom_storage/dom_storage_map.h"
14 #include "webkit/dom_storage/dom_storage_namespace.h" 14 #include "webkit/dom_storage/dom_storage_namespace.h"
15 #include "webkit/dom_storage/dom_storage_task_runner.h" 15 #include "webkit/dom_storage/dom_storage_task_runner.h"
16 #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" 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 25 matching lines...) Expand all
53 } 55 }
54 56
55 DomStorageArea::DomStorageArea(const GURL& origin, const FilePath& directory, 57 DomStorageArea::DomStorageArea(const GURL& origin, const FilePath& directory,
56 DomStorageTaskRunner* task_runner) 58 DomStorageTaskRunner* task_runner)
57 : namespace_id_(kLocalStorageNamespaceId), origin_(origin), 59 : namespace_id_(kLocalStorageNamespaceId), origin_(origin),
58 directory_(directory), 60 directory_(directory),
59 task_runner_(task_runner), 61 task_runner_(task_runner),
60 map_(new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance)), 62 map_(new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance)),
61 is_initial_import_done_(true), 63 is_initial_import_done_(true),
62 is_shutdown_(false), 64 is_shutdown_(false),
63 commit_batches_in_flight_(0) { 65 commit_batches_in_flight_(0),
66 deletion_in_progress_(false) {
64 if (!directory.empty()) { 67 if (!directory.empty()) {
65 FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_)); 68 FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_));
66 backing_.reset(new LocalStorageDatabaseAdapter(path)); 69 backing_.reset(new LocalStorageDatabaseAdapter(path));
67 is_initial_import_done_ = false; 70 is_initial_import_done_ = false;
68 } 71 }
69 } 72 }
70 73
71 DomStorageArea::DomStorageArea( 74 DomStorageArea::DomStorageArea(
72 int64 namespace_id, 75 int64 namespace_id,
73 const std::string& persistent_namespace_id, 76 const std::string& persistent_namespace_id,
74 const GURL& origin, 77 const GURL& origin,
78 SessionStorageDatabase* session_storage_backing,
75 DomStorageTaskRunner* task_runner) 79 DomStorageTaskRunner* task_runner)
76 : namespace_id_(namespace_id), 80 : namespace_id_(namespace_id),
77 persistent_namespace_id_(persistent_namespace_id), 81 persistent_namespace_id_(persistent_namespace_id),
78 origin_(origin), 82 origin_(origin),
79 task_runner_(task_runner), 83 task_runner_(task_runner),
80 map_(new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance)), 84 map_(new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance)),
85 session_storage_backing_(session_storage_backing),
81 is_initial_import_done_(true), 86 is_initial_import_done_(true),
82 is_shutdown_(false), 87 is_shutdown_(false),
83 commit_batches_in_flight_(0) { 88 commit_batches_in_flight_(0),
89 deletion_in_progress_(false) {
84 DCHECK(namespace_id != kLocalStorageNamespaceId); 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 }
85 } 96 }
86 97
87 DomStorageArea::~DomStorageArea() { 98 DomStorageArea::~DomStorageArea() {
88 } 99 }
89 100
90 void DomStorageArea::ExtractValues(ValuesMap* map) { 101 void DomStorageArea::ExtractValues(ValuesMap* map) {
91 if (is_shutdown_) 102 if (is_shutdown_)
92 return; 103 return;
93 InitialImportIfNeeded(); 104 InitialImportIfNeeded();
94 map_->ExtractValues(map); 105 map_->ExtractValues(map);
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
161 } 172 }
162 173
163 return true; 174 return true;
164 } 175 }
165 176
166 DomStorageArea* DomStorageArea::ShallowCopy( 177 DomStorageArea* DomStorageArea::ShallowCopy(
167 int64 destination_namespace_id, 178 int64 destination_namespace_id,
168 const std::string& destination_persistent_namespace_id) { 179 const std::string& destination_persistent_namespace_id) {
169 DCHECK_NE(kLocalStorageNamespaceId, namespace_id_); 180 DCHECK_NE(kLocalStorageNamespaceId, namespace_id_);
170 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id); 181 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id);
171 DCHECK(!backing_.get()); // SessionNamespaces aren't stored on disk.
172 182
173 DomStorageArea* copy = new DomStorageArea( 183 DomStorageArea* copy = new DomStorageArea(
174 destination_namespace_id, destination_persistent_namespace_id, origin_, 184 destination_namespace_id, destination_persistent_namespace_id, origin_,
175 task_runner_); 185 session_storage_backing_, task_runner_);
176 copy->map_ = map_; 186 copy->map_ = map_;
177 copy->is_shutdown_ = is_shutdown_; 187 copy->is_shutdown_ = is_shutdown_;
188 copy->is_initial_import_done_ = true;
189
190 // All the uncommitted changes to this area need to happen before the actual
191 // shallow copy is made (scheduled by the upper layer). Another OnCommitTimer
192 // call might be in the event queue at this point, but it's handled gracefully
193 // when it fires.
194 if (commit_batch_.get())
195 OnCommitTimer();
178 return copy; 196 return copy;
179 } 197 }
180 198
181 bool DomStorageArea::HasUncommittedChanges() const { 199 bool DomStorageArea::HasUncommittedChanges() const {
182 DCHECK(!is_shutdown_); 200 DCHECK(!is_shutdown_);
183 return commit_batch_.get() || commit_batches_in_flight_; 201 return commit_batch_.get() || commit_batches_in_flight_;
184 } 202 }
185 203
186 void DomStorageArea::DeleteOrigin() { 204 void DomStorageArea::DeleteOrigin() {
187 DCHECK(!is_shutdown_); 205 DCHECK(!is_shutdown_);
188 if (HasUncommittedChanges()) { 206 if (HasUncommittedChanges()) {
189 // TODO(michaeln): This logically deletes the data immediately, 207 // TODO(michaeln): This logically deletes the data immediately,
190 // and in a matter of a second, deletes the rows from the backing 208 // and in a matter of a second, deletes the rows from the backing
191 // database file, but the file itself will linger until shutdown 209 // database file, but the file itself will linger until shutdown
192 // or purge time. Ideally, this should delete the file more 210 // or purge time. Ideally, this should delete the file more
193 // quickly. 211 // quickly.
194 Clear(); 212 Clear();
195 return; 213 return;
196 } 214 }
197 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance); 215 map_ = new DomStorageMap(kPerAreaQuota + kPerAreaOverQuotaAllowance);
198 if (backing_.get()) { 216 if (backing_.get() && !session_storage_backing_.get()) {
217 // This is localStorage.
199 is_initial_import_done_ = false; 218 is_initial_import_done_ = false;
200 backing_->Reset(); 219 backing_->Reset();
201 backing_->DeleteFiles(); 220 backing_->DeleteFiles();
221 } else if (session_storage_backing_.get()) {
222 // No need to read the data (there will be no data), also, the
223 // PRIMARY_SEQUENCE thread shouldn't try to read the data while
224 // SessionStorageDatabase::DeleteArea is in progress.
225 is_initial_import_done_ = true;
226 {
227 base::AutoLock lock(deletion_in_progress_lock_);
michaeln 2012/07/10 01:16:58 What does the flag+lock accomplish given that is_i
marja 2012/07/10 13:40:52 The lock was guarding the bool (I didn't want to a
228 deletion_in_progress_ = true;
229 }
230 task_runner_->PostShutdownBlockingTask(
231 FROM_HERE,
232 DomStorageTaskRunner::COMMIT_SEQUENCE,
233 base::Bind(&DomStorageArea::DeleteOriginInCommitSequence, this));
202 } 234 }
203 } 235 }
204 236
205 void DomStorageArea::PurgeMemory() { 237 void DomStorageArea::PurgeMemory() {
206 DCHECK(!is_shutdown_); 238 DCHECK(!is_shutdown_);
207 if (!is_initial_import_done_ || // We're not using any memory. 239 if (!is_initial_import_done_ || // We're not using any memory.
208 !backing_.get() || // We can't purge anything. 240 !backing_.get() || // We can't purge anything.
209 HasUncommittedChanges()) // We leave things alone with changes pending. 241 HasUncommittedChanges()) // We leave things alone with changes pending.
210 return; 242 return;
211 243
(...skipping 17 matching lines...) Expand all
229 FROM_HERE, 261 FROM_HERE,
230 DomStorageTaskRunner::COMMIT_SEQUENCE, 262 DomStorageTaskRunner::COMMIT_SEQUENCE,
231 base::Bind(&DomStorageArea::ShutdownInCommitSequence, this)); 263 base::Bind(&DomStorageArea::ShutdownInCommitSequence, this));
232 DCHECK(success); 264 DCHECK(success);
233 } 265 }
234 266
235 void DomStorageArea::InitialImportIfNeeded() { 267 void DomStorageArea::InitialImportIfNeeded() {
236 if (is_initial_import_done_) 268 if (is_initial_import_done_)
237 return; 269 return;
238 270
239 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_); 271 is_initial_import_done_ = true;
272 {
273 base::AutoLock lock(deletion_in_progress_lock_);
274 if (deletion_in_progress_) {
275 // The data is getting deleted in the commit sequence; we shouldn't read
276 // it just before deletion. The data will be empty, so it's ok to not read
277 // anything.
278 return;
279 }
280 }
281
240 DCHECK(backing_.get()); 282 DCHECK(backing_.get());
241 283
242 ValuesMap initial_values; 284 ValuesMap initial_values;
243 backing_->ReadAllValues(&initial_values); 285 backing_->ReadAllValues(&initial_values);
244 map_->SwapValues(&initial_values); 286 map_->SwapValues(&initial_values);
245 is_initial_import_done_ = true;
246 } 287 }
247 288
248 DomStorageArea::CommitBatch* DomStorageArea::CreateCommitBatchIfNeeded() { 289 DomStorageArea::CommitBatch* DomStorageArea::CreateCommitBatchIfNeeded() {
249 DCHECK(!is_shutdown_); 290 DCHECK(!is_shutdown_);
250 if (!commit_batch_.get()) { 291 if (!commit_batch_.get()) {
251 commit_batch_.reset(new CommitBatch()); 292 commit_batch_.reset(new CommitBatch());
252 293
253 // Start a timer to commit any changes that accrue in the batch, but only if 294 // Start a timer to commit any changes that accrue in the batch, but only if
254 // no commits are currently in flight. In that case the timer will be 295 // no commits are currently in flight. In that case the timer will be
255 // started after the commits have happened. 296 // started after the commits have happened.
256 if (!commit_batches_in_flight_) { 297 if (!commit_batches_in_flight_) {
257 task_runner_->PostDelayedTask( 298 task_runner_->PostDelayedTask(
258 FROM_HERE, 299 FROM_HERE,
259 base::Bind(&DomStorageArea::OnCommitTimer, this), 300 base::Bind(&DomStorageArea::OnCommitTimer, this),
260 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); 301 base::TimeDelta::FromSeconds(kCommitTimerSeconds));
261 } 302 }
262 } 303 }
263 return commit_batch_.get(); 304 return commit_batch_.get();
264 } 305 }
265 306
266 void DomStorageArea::OnCommitTimer() { 307 void DomStorageArea::OnCommitTimer() {
267 DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_);
268 if (is_shutdown_) 308 if (is_shutdown_)
269 return; 309 return;
270 310
271 DCHECK(backing_.get()); 311 DCHECK(backing_.get());
272 DCHECK(commit_batch_.get()); 312
273 DCHECK(!commit_batches_in_flight_); 313 // It's possible that there is nothing to commit, since a shallow copy occured
314 // before the timer fired.
315 if (!commit_batch_.get())
316 return;
274 317
275 // This method executes on the primary sequence, we schedule 318 // This method executes on the primary sequence, we schedule
276 // a task for immediate execution on the commit sequence. 319 // a task for immediate execution on the commit sequence.
277 DCHECK(task_runner_->IsRunningOnPrimarySequence()); 320 DCHECK(task_runner_->IsRunningOnPrimarySequence());
278 bool success = task_runner_->PostShutdownBlockingTask( 321 bool success = task_runner_->PostShutdownBlockingTask(
279 FROM_HERE, 322 FROM_HERE,
280 DomStorageTaskRunner::COMMIT_SEQUENCE, 323 DomStorageTaskRunner::COMMIT_SEQUENCE,
281 base::Bind(&DomStorageArea::CommitChanges, this, 324 base::Bind(&DomStorageArea::CommitChanges, this,
282 base::Owned(commit_batch_.release()))); 325 base::Owned(commit_batch_.release())));
283 ++commit_batches_in_flight_; 326 ++commit_batches_in_flight_;
284 DCHECK(success); 327 DCHECK(success);
285 } 328 }
286 329
287 void DomStorageArea::CommitChanges(const CommitBatch* commit_batch) { 330 void DomStorageArea::CommitChanges(const CommitBatch* commit_batch) {
288 // This method executes on the commit sequence. 331 // This method executes on the commit sequence.
289 DCHECK(task_runner_->IsRunningOnCommitSequence()); 332 DCHECK(task_runner_->IsRunningOnCommitSequence());
290 bool success = backing_->CommitChanges(commit_batch->clear_all_first, 333 bool success = backing_->CommitChanges(commit_batch->clear_all_first,
291 commit_batch->changed_values); 334 commit_batch->changed_values);
292 DCHECK(success); // TODO(michaeln): what if it fails? 335 DCHECK(success); // TODO(michaeln): what if it fails?
293 task_runner_->PostTask( 336 task_runner_->PostTask(
294 FROM_HERE, 337 FROM_HERE,
295 base::Bind(&DomStorageArea::OnCommitComplete, this)); 338 base::Bind(&DomStorageArea::OnCommitComplete, this));
296 } 339 }
297 340
298 void DomStorageArea::OnCommitComplete() { 341 void DomStorageArea::OnCommitComplete() {
299 // We're back on the primary sequence in this method. 342 // We're back on the primary sequence in this method.
300 DCHECK(task_runner_->IsRunningOnPrimarySequence()); 343 DCHECK(task_runner_->IsRunningOnPrimarySequence());
344 --commit_batches_in_flight_;
301 if (is_shutdown_) 345 if (is_shutdown_)
302 return; 346 return;
303 --commit_batches_in_flight_;
304 if (commit_batch_.get() && !commit_batches_in_flight_) { 347 if (commit_batch_.get() && !commit_batches_in_flight_) {
305 // More changes have accrued, restart the timer. 348 // More changes have accrued, restart the timer.
306 task_runner_->PostDelayedTask( 349 task_runner_->PostDelayedTask(
307 FROM_HERE, 350 FROM_HERE,
308 base::Bind(&DomStorageArea::OnCommitTimer, this), 351 base::Bind(&DomStorageArea::OnCommitTimer, this),
309 base::TimeDelta::FromSeconds(kCommitTimerSeconds)); 352 base::TimeDelta::FromSeconds(kCommitTimerSeconds));
310 } 353 }
311 } 354 }
312 355
356 void DomStorageArea::DeleteOriginInCommitSequence() {
357 session_storage_backing_->DeleteArea(persistent_namespace_id_, origin_);
358 base::AutoLock lock(deletion_in_progress_lock_);
359 deletion_in_progress_ = false;
360 }
361
313 void DomStorageArea::ShutdownInCommitSequence() { 362 void DomStorageArea::ShutdownInCommitSequence() {
314 // This method executes on the commit sequence. 363 // This method executes on the commit sequence.
315 DCHECK(task_runner_->IsRunningOnCommitSequence()); 364 DCHECK(task_runner_->IsRunningOnCommitSequence());
316 DCHECK(backing_.get()); 365 DCHECK(backing_.get());
317 if (commit_batch_.get()) { 366 if (commit_batch_.get()) {
318 // Commit any changes that accrued prior to the timer firing. 367 // Commit any changes that accrued prior to the timer firing.
319 bool success = backing_->CommitChanges( 368 bool success = backing_->CommitChanges(
320 commit_batch_->clear_all_first, 369 commit_batch_->clear_all_first,
321 commit_batch_->changed_values); 370 commit_batch_->changed_values);
322 DCHECK(success); 371 DCHECK(success);
323 } 372 }
324 commit_batch_.reset(); 373 commit_batch_.reset();
325 backing_.reset(); 374 backing_.reset();
375 session_storage_backing_ = NULL;
326 } 376 }
327 377
328 } // namespace dom_storage 378 } // namespace dom_storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698