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

Side by Side Diff: content/browser/dom_storage/dom_storage_area.cc

Issue 896643002: [DOMStorage] Rate limiting writes to disk. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: code review comments Created 5 years, 10 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
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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/dom_storage/dom_storage_area.h" 5 #include "content/browser/dom_storage/dom_storage_area.h"
6 6
7 #include <algorithm>
8
7 #include "base/bind.h" 9 #include "base/bind.h"
8 #include "base/location.h" 10 #include "base/location.h"
9 #include "base/logging.h" 11 #include "base/logging.h"
10 #include "base/metrics/histogram.h" 12 #include "base/metrics/histogram.h"
11 #include "base/strings/utf_string_conversions.h" 13 #include "base/strings/utf_string_conversions.h"
14 #include "base/sys_info.h"
12 #include "base/time/time.h" 15 #include "base/time/time.h"
13 #include "content/browser/dom_storage/dom_storage_namespace.h" 16 #include "content/browser/dom_storage/dom_storage_namespace.h"
14 #include "content/browser/dom_storage/dom_storage_task_runner.h" 17 #include "content/browser/dom_storage/dom_storage_task_runner.h"
15 #include "content/browser/dom_storage/local_storage_database_adapter.h" 18 #include "content/browser/dom_storage/local_storage_database_adapter.h"
16 #include "content/browser/dom_storage/session_storage_database.h" 19 #include "content/browser/dom_storage/session_storage_database.h"
17 #include "content/browser/dom_storage/session_storage_database_adapter.h" 20 #include "content/browser/dom_storage/session_storage_database_adapter.h"
18 #include "content/common/dom_storage/dom_storage_map.h" 21 #include "content/common/dom_storage/dom_storage_map.h"
19 #include "content/common/dom_storage/dom_storage_types.h" 22 #include "content/common/dom_storage/dom_storage_types.h"
20 #include "storage/browser/database/database_util.h" 23 #include "storage/browser/database/database_util.h"
21 #include "storage/common/database/database_identifier.h" 24 #include "storage/common/database/database_identifier.h"
22 #include "storage/common/fileapi/file_system_util.h" 25 #include "storage/common/fileapi/file_system_util.h"
23 26
24 using storage::DatabaseUtil; 27 using storage::DatabaseUtil;
25 28
26 namespace content { 29 namespace content {
27 30
28 static const int kCommitTimerSeconds = 1; 31 // Delay for a moment after a value is set in anticipation
32 // of other values being set, so changes are batched.
33 static const int kCommitDefaultDelaySecs = 5;
Alexei Svitkine (slow) 2015/02/17 01:05:20 Nit: Move these constants to the anonymous namespa
michaeln 2015/02/19 01:19:20 Done.
29 34
30 DOMStorageArea::CommitBatch::CommitBatch() 35 // The delay used if a commit is scheduled during startup, also the
31 : clear_all_first(false) { 36 // process uptime duration used as a proxy for 'during startup'.
37 static const int kCommitDuringStartupDelaySecs = 60;
38
39 // To avoid excessive IO we apply limits to the amount of data being written
40 // and the frequency of writes. The specific values used are somewhat arbitrary.
41 static const int kMaxBytesPerDay = kPerStorageAreaQuota * 2;
42 static const int kMaxCommitsPerHour = 6;
43
44 namespace {
45
46 // TODO(michaeln): Put this somewhere appropiate for reuse.
47 bool IsBrowserStartingUp() {
Alexei Svitkine (slow) 2015/02/17 01:05:20 Nit: It seems like this function should instead be
michaeln 2015/02/17 19:36:51 Oh! Thank you for this comment. I mistakenly thoug
michaeln 2015/02/19 01:20:03 Done.
48 return base::SysInfo::Uptime() < kCommitDuringStartupDelaySecs * 1000;
49 }
50
51 }
Alexei Svitkine (slow) 2015/02/17 01:05:20 Nit: add " // namespace"
michaeln 2015/02/19 01:19:20 Done.
52
53 DOMStorageArea::RateLimiter::RateLimiter(size_t desired_rate,
54 base::TimeDelta time_quantum)
55 : rate_(desired_rate), samples_(0), time_quantum_(time_quantum) {
56 DCHECK_GT(desired_rate, 0ul);
57 }
58
59 void DOMStorageArea::RateLimiter::add_samples(size_t samples) {
Alexei Svitkine (slow) 2015/02/17 01:05:20 Nit: Either inline this into the header or name it
michaeln 2015/02/19 01:19:20 Done.
60 samples_ += samples;
61 }
62
63 // Computes the total time needed to process the total samples seen
64 // at the desired rate.
65 base::TimeDelta DOMStorageArea::RateLimiter::ComputeTimeNeeded() const {
66 return base::TimeDelta::FromInternalValue((samples_ / rate_) *
Alexei Svitkine (slow) 2015/02/17 01:05:20 The comment from time.h says this about ToInternal
michaeln 2015/02/17 19:36:51 Nice idea. There isn't an operator that takes floa
67 time_quantum_.ToInternalValue());
68 }
69
70 // Given the elapsed time since the start of the rate limiting session,
71 // computes the delay needed to mimic having processed the total samples
72 // seen at the desired rate.
Alexei Svitkine (slow) 2015/02/17 01:05:20 Nit: Comments should be in the header file. Same a
michaeln 2015/02/19 01:19:20 Done.
73 base::TimeDelta DOMStorageArea::RateLimiter::ComputeDelayNeeded(
74 const base::TimeDelta elapsed_time) const {
75 base::TimeDelta time_needed = ComputeTimeNeeded();
76 if (time_needed > elapsed_time)
77 return time_needed - elapsed_time;
78 return base::TimeDelta();
79 }
80
81 DOMStorageArea::CommitBatch::CommitBatch() : clear_all_first(false) {
32 } 82 }
33 DOMStorageArea::CommitBatch::~CommitBatch() {} 83 DOMStorageArea::CommitBatch::~CommitBatch() {}
34 84
85 size_t DOMStorageArea::CommitBatch::GetDataSize() const {
86 return DOMStorageMap::CountBytes(changed_values);
87 }
35 88
36 // static 89 // static
37 const base::FilePath::CharType DOMStorageArea::kDatabaseFileExtension[] = 90 const base::FilePath::CharType DOMStorageArea::kDatabaseFileExtension[] =
38 FILE_PATH_LITERAL(".localstorage"); 91 FILE_PATH_LITERAL(".localstorage");
39 92
40 // static 93 // static
41 base::FilePath DOMStorageArea::DatabaseFileNameFromOrigin(const GURL& origin) { 94 base::FilePath DOMStorageArea::DatabaseFileNameFromOrigin(const GURL& origin) {
42 std::string filename = storage::GetIdentifierFromOrigin(origin); 95 std::string filename = storage::GetIdentifierFromOrigin(origin);
43 // There is no base::FilePath.AppendExtension() method, so start with just the 96 // There is no base::FilePath.AppendExtension() method, so start with just the
44 // extension as the filename, and then InsertBeforeExtension the desired 97 // extension as the filename, and then InsertBeforeExtension the desired
45 // name. 98 // name.
46 return base::FilePath().Append(kDatabaseFileExtension). 99 return base::FilePath().Append(kDatabaseFileExtension).
47 InsertBeforeExtensionASCII(filename); 100 InsertBeforeExtensionASCII(filename);
48 } 101 }
49 102
50 // static 103 // static
51 GURL DOMStorageArea::OriginFromDatabaseFileName(const base::FilePath& name) { 104 GURL DOMStorageArea::OriginFromDatabaseFileName(const base::FilePath& name) {
52 DCHECK(name.MatchesExtension(kDatabaseFileExtension)); 105 DCHECK(name.MatchesExtension(kDatabaseFileExtension));
53 std::string origin_id = 106 std::string origin_id =
54 name.BaseName().RemoveExtension().MaybeAsASCII(); 107 name.BaseName().RemoveExtension().MaybeAsASCII();
55 return storage::GetOriginFromIdentifier(origin_id); 108 return storage::GetOriginFromIdentifier(origin_id);
56 } 109 }
57 110
58 DOMStorageArea::DOMStorageArea( 111 DOMStorageArea::DOMStorageArea(const GURL& origin,
59 const GURL& origin, const base::FilePath& directory, 112 const base::FilePath& directory,
60 DOMStorageTaskRunner* task_runner) 113 DOMStorageTaskRunner* task_runner)
61 : namespace_id_(kLocalStorageNamespaceId), origin_(origin), 114 : namespace_id_(kLocalStorageNamespaceId),
115 origin_(origin),
62 directory_(directory), 116 directory_(directory),
63 task_runner_(task_runner), 117 task_runner_(task_runner),
64 map_(new DOMStorageMap(kPerStorageAreaQuota + 118 map_(new DOMStorageMap(kPerStorageAreaQuota +
65 kPerStorageAreaOverQuotaAllowance)), 119 kPerStorageAreaOverQuotaAllowance)),
66 is_initial_import_done_(true), 120 is_initial_import_done_(true),
67 is_shutdown_(false), 121 is_shutdown_(false),
68 commit_batches_in_flight_(0) { 122 commit_batches_in_flight_(0),
123 start_time_(base::TimeTicks::Now()),
124 data_rate_limiter_(kMaxBytesPerDay, base::TimeDelta::FromHours(24)),
125 commit_rate_limiter_(kMaxCommitsPerHour, base::TimeDelta::FromHours(1)) {
69 if (!directory.empty()) { 126 if (!directory.empty()) {
70 base::FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_)); 127 base::FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_));
71 backing_.reset(new LocalStorageDatabaseAdapter(path)); 128 backing_.reset(new LocalStorageDatabaseAdapter(path));
72 is_initial_import_done_ = false; 129 is_initial_import_done_ = false;
73 } 130 }
74 } 131 }
75 132
76 DOMStorageArea::DOMStorageArea( 133 DOMStorageArea::DOMStorageArea(int64 namespace_id,
77 int64 namespace_id, 134 const std::string& persistent_namespace_id,
78 const std::string& persistent_namespace_id, 135 const GURL& origin,
79 const GURL& origin, 136 SessionStorageDatabase* session_storage_backing,
80 SessionStorageDatabase* session_storage_backing, 137 DOMStorageTaskRunner* task_runner)
81 DOMStorageTaskRunner* task_runner)
82 : namespace_id_(namespace_id), 138 : namespace_id_(namespace_id),
83 persistent_namespace_id_(persistent_namespace_id), 139 persistent_namespace_id_(persistent_namespace_id),
84 origin_(origin), 140 origin_(origin),
85 task_runner_(task_runner), 141 task_runner_(task_runner),
86 map_(new DOMStorageMap(kPerStorageAreaQuota + 142 map_(new DOMStorageMap(kPerStorageAreaQuota +
87 kPerStorageAreaOverQuotaAllowance)), 143 kPerStorageAreaOverQuotaAllowance)),
88 session_storage_backing_(session_storage_backing), 144 session_storage_backing_(session_storage_backing),
89 is_initial_import_done_(true), 145 is_initial_import_done_(true),
90 is_shutdown_(false), 146 is_shutdown_(false),
91 commit_batches_in_flight_(0) { 147 commit_batches_in_flight_(0),
148 start_time_(base::TimeTicks::Now()),
149 data_rate_limiter_(kMaxBytesPerDay, base::TimeDelta::FromHours(24)),
150 commit_rate_limiter_(kMaxCommitsPerHour, base::TimeDelta::FromHours(1)) {
92 DCHECK(namespace_id != kLocalStorageNamespaceId); 151 DCHECK(namespace_id != kLocalStorageNamespaceId);
93 if (session_storage_backing) { 152 if (session_storage_backing) {
94 backing_.reset(new SessionStorageDatabaseAdapter( 153 backing_.reset(new SessionStorageDatabaseAdapter(
95 session_storage_backing, persistent_namespace_id, origin)); 154 session_storage_backing, persistent_namespace_id, origin));
96 is_initial_import_done_ = false; 155 is_initial_import_done_ = false;
97 } 156 }
98 } 157 }
99 158
100 DOMStorageArea::~DOMStorageArea() { 159 DOMStorageArea::~DOMStorageArea() {
101 } 160 }
(...skipping 28 matching lines...) Expand all
130 189
131 bool DOMStorageArea::SetItem(const base::string16& key, 190 bool DOMStorageArea::SetItem(const base::string16& key,
132 const base::string16& value, 191 const base::string16& value,
133 base::NullableString16* old_value) { 192 base::NullableString16* old_value) {
134 if (is_shutdown_) 193 if (is_shutdown_)
135 return false; 194 return false;
136 InitialImportIfNeeded(); 195 InitialImportIfNeeded();
137 if (!map_->HasOneRef()) 196 if (!map_->HasOneRef())
138 map_ = map_->DeepCopy(); 197 map_ = map_->DeepCopy();
139 bool success = map_->SetItem(key, value, old_value); 198 bool success = map_->SetItem(key, value, old_value);
140 if (success && backing_) { 199 if (success && backing_ &&
200 (old_value->is_null() || old_value->string() != value)) {
141 CommitBatch* commit_batch = CreateCommitBatchIfNeeded(); 201 CommitBatch* commit_batch = CreateCommitBatchIfNeeded();
142 commit_batch->changed_values[key] = base::NullableString16(value, false); 202 commit_batch->changed_values[key] = base::NullableString16(value, false);
143 } 203 }
144 return success; 204 return success;
145 } 205 }
146 206
147 bool DOMStorageArea::RemoveItem(const base::string16& key, 207 bool DOMStorageArea::RemoveItem(const base::string16& key,
148 base::string16* old_value) { 208 base::string16* old_value) {
149 if (is_shutdown_) 209 if (is_shutdown_)
150 return false; 210 return false;
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
205 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id); 265 DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id);
206 266
207 DOMStorageArea* copy = new DOMStorageArea( 267 DOMStorageArea* copy = new DOMStorageArea(
208 destination_namespace_id, destination_persistent_namespace_id, origin_, 268 destination_namespace_id, destination_persistent_namespace_id, origin_,
209 session_storage_backing_.get(), task_runner_.get()); 269 session_storage_backing_.get(), task_runner_.get());
210 copy->map_ = map_; 270 copy->map_ = map_;
211 copy->is_shutdown_ = is_shutdown_; 271 copy->is_shutdown_ = is_shutdown_;
212 copy->is_initial_import_done_ = true; 272 copy->is_initial_import_done_ = true;
213 273
214 // All the uncommitted changes to this area need to happen before the actual 274 // All the uncommitted changes to this area need to happen before the actual
215 // shallow copy is made (scheduled by the upper layer). Another OnCommitTimer 275 // shallow copy is made (scheduled by the upper layer sometime after return).
216 // call might be in the event queue at this point, but it's handled gracefully
217 // when it fires.
218 if (commit_batch_) 276 if (commit_batch_)
219 OnCommitTimer(); 277 ScheduleImmediateCommit();
220 return copy; 278 return copy;
221 } 279 }
222 280
223 bool DOMStorageArea::HasUncommittedChanges() const { 281 bool DOMStorageArea::HasUncommittedChanges() const {
224 DCHECK(!is_shutdown_);
225 return commit_batch_.get() || commit_batches_in_flight_; 282 return commit_batch_.get() || commit_batches_in_flight_;
226 } 283 }
227 284
285 void DOMStorageArea::ScheduleImmediateCommit() {
286 DCHECK(HasUncommittedChanges());
287 PostCommitTask();
288 }
289
228 void DOMStorageArea::DeleteOrigin() { 290 void DOMStorageArea::DeleteOrigin() {
229 DCHECK(!is_shutdown_); 291 DCHECK(!is_shutdown_);
230 // This function shouldn't be called for sessionStorage. 292 // This function shouldn't be called for sessionStorage.
231 DCHECK(!session_storage_backing_.get()); 293 DCHECK(!session_storage_backing_.get());
232 if (HasUncommittedChanges()) { 294 if (HasUncommittedChanges()) {
233 // TODO(michaeln): This logically deletes the data immediately, 295 // TODO(michaeln): This logically deletes the data immediately,
234 // and in a matter of a second, deletes the rows from the backing 296 // and in a matter of a second, deletes the rows from the backing
235 // database file, but the file itself will linger until shutdown 297 // database file, but the file itself will linger until shutdown
236 // or purge time. Ideally, this should delete the file more 298 // or purge time. Ideally, this should delete the file more
237 // quickly. 299 // quickly.
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
320 DOMStorageArea::CommitBatch* DOMStorageArea::CreateCommitBatchIfNeeded() { 382 DOMStorageArea::CommitBatch* DOMStorageArea::CreateCommitBatchIfNeeded() {
321 DCHECK(!is_shutdown_); 383 DCHECK(!is_shutdown_);
322 if (!commit_batch_) { 384 if (!commit_batch_) {
323 commit_batch_.reset(new CommitBatch()); 385 commit_batch_.reset(new CommitBatch());
324 386
325 // Start a timer to commit any changes that accrue in the batch, but only if 387 // Start a timer to commit any changes that accrue in the batch, but only if
326 // no commits are currently in flight. In that case the timer will be 388 // no commits are currently in flight. In that case the timer will be
327 // started after the commits have happened. 389 // started after the commits have happened.
328 if (!commit_batches_in_flight_) { 390 if (!commit_batches_in_flight_) {
329 task_runner_->PostDelayedTask( 391 task_runner_->PostDelayedTask(
330 FROM_HERE, 392 FROM_HERE, base::Bind(&DOMStorageArea::OnCommitTimer, this),
331 base::Bind(&DOMStorageArea::OnCommitTimer, this), 393 ComputeCommitDelay());
332 base::TimeDelta::FromSeconds(kCommitTimerSeconds));
333 } 394 }
334 } 395 }
335 return commit_batch_.get(); 396 return commit_batch_.get();
336 } 397 }
337 398
399 base::TimeDelta DOMStorageArea::ComputeCommitDelay() const {
400 base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time_;
401 base::TimeDelta delay = std::max(
402 base::TimeDelta::FromSeconds(kCommitDefaultDelaySecs),
403 std::max(commit_rate_limiter_.ComputeDelayNeeded(elapsed_time),
404 data_rate_limiter_.ComputeDelayNeeded(elapsed_time)));
405 UMA_HISTOGRAM_LONG_TIMES("LocalStorage.CommitDelay", delay);
406 return delay;
407 }
408
338 void DOMStorageArea::OnCommitTimer() { 409 void DOMStorageArea::OnCommitTimer() {
339 if (is_shutdown_) 410 if (is_shutdown_)
340 return; 411 return;
341 412
413 // It's possible that there is nothing to commit if an immediate
414 // commit occured after the timer was scheduled but before it fired.
415 if (!commit_batch_)
416 return;
417
418 // Don't mind the double counting of the few that are deferred compared
419 // to the masses that aren't.
420 bool defer_commit = IsBrowserStartingUp();
421 UMA_HISTOGRAM_BOOLEAN("LocalStorage.Commit", !defer_commit);
422 if (defer_commit) {
423 task_runner_->PostDelayedTask(
424 FROM_HERE, base::Bind(&DOMStorageArea::OnCommitTimer, this),
425 base::TimeDelta::FromSeconds(kCommitDuringStartupDelaySecs));
426 return;
427 }
428
429 PostCommitTask();
430 }
431
432 void DOMStorageArea::PostCommitTask() {
433 if (is_shutdown_ || !commit_batch_)
434 return;
435
342 DCHECK(backing_.get()); 436 DCHECK(backing_.get());
343 437
344 // It's possible that there is nothing to commit, since a shallow copy occured 438 commit_rate_limiter_.add_samples(1);
345 // before the timer fired. 439 data_rate_limiter_.add_samples(commit_batch_->GetDataSize());
346 if (!commit_batch_)
347 return;
348 440
349 // This method executes on the primary sequence, we schedule 441 // This method executes on the primary sequence, we schedule
350 // a task for immediate execution on the commit sequence. 442 // a task for immediate execution on the commit sequence.
351 DCHECK(task_runner_->IsRunningOnPrimarySequence()); 443 DCHECK(task_runner_->IsRunningOnPrimarySequence());
352 bool success = task_runner_->PostShutdownBlockingTask( 444 bool success = task_runner_->PostShutdownBlockingTask(
353 FROM_HERE, 445 FROM_HERE,
354 DOMStorageTaskRunner::COMMIT_SEQUENCE, 446 DOMStorageTaskRunner::COMMIT_SEQUENCE,
355 base::Bind(&DOMStorageArea::CommitChanges, this, 447 base::Bind(&DOMStorageArea::CommitChanges, this,
356 base::Owned(commit_batch_.release()))); 448 base::Owned(commit_batch_.release())));
357 ++commit_batches_in_flight_; 449 ++commit_batches_in_flight_;
358 DCHECK(success); 450 DCHECK(success);
359 } 451 }
360 452
361 void DOMStorageArea::CommitChanges(const CommitBatch* commit_batch) { 453 void DOMStorageArea::CommitChanges(const CommitBatch* commit_batch) {
362 // This method executes on the commit sequence. 454 // This method executes on the commit sequence.
363 DCHECK(task_runner_->IsRunningOnCommitSequence()); 455 DCHECK(task_runner_->IsRunningOnCommitSequence());
364 backing_->CommitChanges(commit_batch->clear_all_first, 456 backing_->CommitChanges(commit_batch->clear_all_first,
365 commit_batch->changed_values); 457 commit_batch->changed_values);
366 // TODO(michaeln): what if CommitChanges returns false (e.g., we're trying to 458 // TODO(michaeln): what if CommitChanges returns false (e.g., we're trying to
367 // commit to a DB which is in an inconsistent state?) 459 // commit to a DB which is in an inconsistent state?)
368 task_runner_->PostTask( 460 task_runner_->PostTask(
369 FROM_HERE, 461 FROM_HERE,
370 base::Bind(&DOMStorageArea::OnCommitComplete, this)); 462 base::Bind(&DOMStorageArea::OnCommitComplete, this));
371 } 463 }
372 464
373 void DOMStorageArea::OnCommitComplete() { 465 void DOMStorageArea::OnCommitComplete() {
374 // We're back on the primary sequence in this method. 466 // We're back on the primary sequence in this method.
375 DCHECK(task_runner_->IsRunningOnPrimarySequence()); 467 DCHECK(task_runner_->IsRunningOnPrimarySequence());
376 --commit_batches_in_flight_; 468 --commit_batches_in_flight_;
377 if (is_shutdown_) 469 if (is_shutdown_)
378 return; 470 return;
379 if (commit_batch_.get() && !commit_batches_in_flight_) { 471 if (commit_batch_.get() && !commit_batches_in_flight_) {
380 // More changes have accrued, restart the timer. 472 // More changes have accrued, restart the timer.
381 task_runner_->PostDelayedTask( 473 task_runner_->PostDelayedTask(
382 FROM_HERE, 474 FROM_HERE, base::Bind(&DOMStorageArea::OnCommitTimer, this),
383 base::Bind(&DOMStorageArea::OnCommitTimer, this), 475 ComputeCommitDelay());
384 base::TimeDelta::FromSeconds(kCommitTimerSeconds));
385 } 476 }
386 } 477 }
387 478
388 void DOMStorageArea::ShutdownInCommitSequence() { 479 void DOMStorageArea::ShutdownInCommitSequence() {
389 // This method executes on the commit sequence. 480 // This method executes on the commit sequence.
390 DCHECK(task_runner_->IsRunningOnCommitSequence()); 481 DCHECK(task_runner_->IsRunningOnCommitSequence());
391 DCHECK(backing_.get()); 482 DCHECK(backing_.get());
392 if (commit_batch_) { 483 if (commit_batch_) {
393 // Commit any changes that accrued prior to the timer firing. 484 // Commit any changes that accrued prior to the timer firing.
394 bool success = backing_->CommitChanges( 485 bool success = backing_->CommitChanges(
395 commit_batch_->clear_all_first, 486 commit_batch_->clear_all_first,
396 commit_batch_->changed_values); 487 commit_batch_->changed_values);
397 DCHECK(success); 488 DCHECK(success);
398 } 489 }
399 commit_batch_.reset(); 490 commit_batch_.reset();
400 backing_.reset(); 491 backing_.reset();
401 session_storage_backing_ = NULL; 492 session_storage_backing_ = NULL;
402 } 493 }
403 494
404 } // namespace content 495 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698