Chromium Code Reviews| Index: content/browser/dom_storage/dom_storage_area.cc | 
| diff --git a/content/browser/dom_storage/dom_storage_area.cc b/content/browser/dom_storage/dom_storage_area.cc | 
| index 90a55a0ed8bddbfc261ee5386c2ba89e698f8da5..ae47f71aefff8594ce42b9b4b9d7c7ce0ef1c41c 100644 | 
| --- a/content/browser/dom_storage/dom_storage_area.cc | 
| +++ b/content/browser/dom_storage/dom_storage_area.cc | 
| @@ -4,6 +4,8 @@ | 
| #include "content/browser/dom_storage/dom_storage_area.h" | 
| +#include <algorithm> | 
| + | 
| #include "base/bind.h" | 
| #include "base/location.h" | 
| #include "base/logging.h" | 
| @@ -25,13 +27,27 @@ using storage::DatabaseUtil; | 
| namespace content { | 
| -static const int kCommitTimerSeconds = 1; | 
| +// Delay for a moment after a value is set in anticipation | 
| +// of other values being set, so changes are batched. | 
| +static const int kCommitTimerDefaultDelay = 5; | 
| + | 
| +// Avoid committing too frequently regardless of the amount of data | 
| +// being written. | 
| +static const int kMaxCommitsPerHour = 6; | 
| + | 
| +// A data rate limit applies to the size of the key/value pairs being written. | 
| +// A rate of 500k per hour is enough to fully populate an origins area two | 
| +// times over. | 
| +static const int kMaxDataPerHour = 500 * 1024;; | 
| 
 
cmumford
2015/02/03 00:45:24
double semicolon
 
michaeln
2015/02/03 19:59:14
Done. Also made this a function of the constant pe
 
 | 
| DOMStorageArea::CommitBatch::CommitBatch() | 
| : clear_all_first(false) { | 
| } | 
| DOMStorageArea::CommitBatch::~CommitBatch() {} | 
| +size_t DOMStorageArea::CommitBatch::GetDataSize() { | 
| + return DOMStorageMap::CountBytes(changed_values); | 
| +} | 
| // static | 
| const base::FilePath::CharType DOMStorageArea::kDatabaseFileExtension[] = | 
| @@ -65,7 +81,10 @@ DOMStorageArea::DOMStorageArea( | 
| kPerStorageAreaOverQuotaAllowance)), | 
| is_initial_import_done_(true), | 
| is_shutdown_(false), | 
| - commit_batches_in_flight_(0) { | 
| + commit_batches_in_flight_(0), | 
| + start_time_(base::Time::Now()), | 
| + data_rate_limiter_(kMaxDataPerHour, base::TimeDelta::FromHours(1)), | 
| + commit_rate_limiter_(kMaxCommitsPerHour, base::TimeDelta::FromHours(1)) { | 
| if (!directory.empty()) { | 
| base::FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_)); | 
| backing_.reset(new LocalStorageDatabaseAdapter(path)); | 
| @@ -88,7 +107,10 @@ DOMStorageArea::DOMStorageArea( | 
| session_storage_backing_(session_storage_backing), | 
| is_initial_import_done_(true), | 
| is_shutdown_(false), | 
| - commit_batches_in_flight_(0) { | 
| + commit_batches_in_flight_(0), | 
| + start_time_(base::Time::Now()), | 
| + data_rate_limiter_(kMaxDataPerHour, base::TimeDelta::FromHours(1)), | 
| + commit_rate_limiter_(kMaxCommitsPerHour, base::TimeDelta::FromHours(1)) { | 
| DCHECK(namespace_id != kLocalStorageNamespaceId); | 
| if (session_storage_backing) { | 
| backing_.reset(new SessionStorageDatabaseAdapter( | 
| @@ -137,7 +159,8 @@ bool DOMStorageArea::SetItem(const base::string16& key, | 
| if (!map_->HasOneRef()) | 
| map_ = map_->DeepCopy(); | 
| bool success = map_->SetItem(key, value, old_value); | 
| - if (success && backing_) { | 
| + if (success && backing_ && | 
| + (old_value->is_null() || old_value->string() != value)) { | 
| CommitBatch* commit_batch = CreateCommitBatchIfNeeded(); | 
| commit_batch->changed_values[key] = base::NullableString16(value, false); | 
| } | 
| @@ -329,12 +352,20 @@ DOMStorageArea::CommitBatch* DOMStorageArea::CreateCommitBatchIfNeeded() { | 
| task_runner_->PostDelayedTask( | 
| FROM_HERE, | 
| base::Bind(&DOMStorageArea::OnCommitTimer, this), | 
| - base::TimeDelta::FromSeconds(kCommitTimerSeconds)); | 
| + ComputeCommitDelay()); | 
| } | 
| } | 
| return commit_batch_.get(); | 
| } | 
| +base::TimeDelta DOMStorageArea::ComputeCommitDelay() { | 
| + base::TimeDelta elapsed_time = base::Time::Now() - start_time_; | 
| + return std::max( | 
| + base::TimeDelta::FromSeconds(kCommitTimerDefaultDelay), | 
| + std::max(commit_rate_limiter_.ComputeDelayNeeded(elapsed_time), | 
| + data_rate_limiter_.ComputeDelayNeeded(elapsed_time))); | 
| +} | 
| + | 
| void DOMStorageArea::OnCommitTimer() { | 
| if (is_shutdown_) | 
| return; | 
| @@ -346,6 +377,9 @@ void DOMStorageArea::OnCommitTimer() { | 
| if (!commit_batch_) | 
| return; | 
| + commit_rate_limiter_.AddSamples(1); | 
| + data_rate_limiter_.AddSamples(commit_batch_->GetDataSize()); | 
| + | 
| // This method executes on the primary sequence, we schedule | 
| // a task for immediate execution on the commit sequence. | 
| DCHECK(task_runner_->IsRunningOnPrimarySequence()); | 
| @@ -381,7 +415,7 @@ void DOMStorageArea::OnCommitComplete() { | 
| task_runner_->PostDelayedTask( | 
| FROM_HERE, | 
| base::Bind(&DOMStorageArea::OnCommitTimer, this), | 
| - base::TimeDelta::FromSeconds(kCommitTimerSeconds)); | 
| + ComputeCommitDelay()); | 
| } | 
| } |