Chromium Code Reviews| Index: webkit/dom_storage/dom_storage_area.cc |
| diff --git a/webkit/dom_storage/dom_storage_area.cc b/webkit/dom_storage/dom_storage_area.cc |
| index c152f3fa44aecbeb5de38e045c2429911be3e86f..576f18a438572b9e96e49834fdf2c08df7640dcf 100644 |
| --- a/webkit/dom_storage/dom_storage_area.cc |
| +++ b/webkit/dom_storage/dom_storage_area.cc |
| @@ -3,8 +3,12 @@ |
| // found in the LICENSE file. |
| #include "webkit/dom_storage/dom_storage_area.h" |
| + |
| +#include "base/bind.h" |
| #include "webkit/dom_storage/dom_storage_map.h" |
| #include "webkit/dom_storage/dom_storage_namespace.h" |
| +#include "webkit/dom_storage/dom_storage_task_runner.h" |
| +#include "webkit/fileapi/file_system_util.h" |
| namespace dom_storage { |
| @@ -13,44 +17,88 @@ DomStorageArea::DomStorageArea( |
| const FilePath& directory, DomStorageTaskRunner* task_runner) |
| : namespace_id_(namespace_id), origin_(origin), |
| directory_(directory), task_runner_(task_runner), |
| - map_(new DomStorageMap()) { |
| + map_(new DomStorageMap()), |
| + backing_(NULL), |
| + initial_import_done_(false), |
| + clear_all_next_commit_(false) { |
| + |
| + if (namespace_id == kLocalStorageNamespaceId && !directory.empty()) { |
| + FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_)); |
| + backing_.reset(new DomStorageDatabase(path)); |
| + } else { |
| + // Not a local storage area or no directory specified for backing |
| + // database, (i.e. it's an incognito profile). |
| + initial_import_done_ = true; |
| + } |
| } |
| DomStorageArea::~DomStorageArea() { |
| + if (clear_all_next_commit_ || !changed_values_.empty()) { |
| + // Still some data left that was not committed to disk, try |
| + // now. |
| + CommitChanges(); |
| + } |
| + // TODO(benm): What if the last commit failed and there's still data |
| + // left not synced to disk? |
| + DCHECK(!clear_all_next_commit_); |
| + DCHECK(changed_values_.empty()); |
| } |
| unsigned DomStorageArea::Length() { |
| + InitialImportIfNeeded(); |
| return map_->Length(); |
| } |
| NullableString16 DomStorageArea::Key(unsigned index) { |
| + InitialImportIfNeeded(); |
| return map_->Key(index); |
| } |
| NullableString16 DomStorageArea::GetItem(const string16& key) { |
| + InitialImportIfNeeded(); |
| return map_->GetItem(key); |
| } |
| -bool DomStorageArea::SetItem( |
| - const string16& key, const string16& value, |
| - NullableString16* old_value) { |
| +bool DomStorageArea::SetItem(const string16& key, |
| + const string16& value, |
| + NullableString16* old_value) { |
| + InitialImportIfNeeded(); |
| + |
| if (!map_->HasOneRef()) |
| map_ = map_->DeepCopy(); |
| - return map_->SetItem(key, value, old_value); |
| + bool success = map_->SetItem(key, value, old_value); |
| + if (success && backing_.get()) { |
| + changed_values_[key] = NullableString16(value, false); |
| + ScheduleCommitChanges(); |
| + } |
| + return success; |
| } |
| -bool DomStorageArea::RemoveItem( |
| - const string16& key, |
| - string16* old_value) { |
| +bool DomStorageArea::RemoveItem(const string16& key, string16* old_value) { |
| + InitialImportIfNeeded(); |
| if (!map_->HasOneRef()) |
| map_ = map_->DeepCopy(); |
| - return map_->RemoveItem(key, old_value); |
| + bool success = map_->RemoveItem(key, old_value); |
| + if (success && backing_.get()) { |
| + changed_values_[key] = NullableString16(true); |
| + ScheduleCommitChanges(); |
| + } |
| + return success; |
| } |
| bool DomStorageArea::Clear() { |
| + InitialImportIfNeeded(); |
| if (map_->Length() == 0) |
| return false; |
| + |
| map_ = new DomStorageMap(); |
| + |
| + if (backing_.get()) { |
| + changed_values_.clear(); |
| + clear_all_next_commit_ = true; |
| + ScheduleCommitChanges(); |
| + } |
| + |
| return true; |
| } |
| @@ -58,10 +106,56 @@ DomStorageArea* DomStorageArea::ShallowCopy(int64 destination_namespace_id) { |
| DCHECK_NE(kLocalStorageNamespaceId, namespace_id_); |
| DCHECK_NE(kLocalStorageNamespaceId, destination_namespace_id); |
| // SessionNamespaces aren't backed by files on disk. |
| + DCHECK(!backing_.get()); |
| + |
| DomStorageArea* copy = new DomStorageArea(destination_namespace_id, origin_, |
| FilePath(), task_runner_); |
| copy->map_ = map_; |
| return copy; |
| } |
| +void DomStorageArea::InitialImportIfNeeded() { |
| + if (initial_import_done_) |
| + return; |
| + |
| + DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_); |
| + DCHECK(backing_.get()); |
| + |
| + ValuesMap initial_values; |
| + backing_->ReadAllValues(&initial_values); |
| + map_->SwapValues(&initial_values); |
| + initial_import_done_ = true; |
| +} |
| + |
| +void DomStorageArea::ScheduleCommitChanges() { |
| + DCHECK_EQ(kLocalStorageNamespaceId, namespace_id_); |
| + DCHECK(backing_.get()); |
| + |
| + if (!clear_all_next_commit_ && changed_values_.empty()) |
|
michaeln
2012/02/15 04:10:53
given the usage of this method, it might make more
benm (inactive)
2012/02/15 17:38:17
sgtm.
|
| + return; |
| + |
| + DCHECK(task_runner_.get()); |
| + if (!task_runner_->PostTask( |
|
michaeln
2012/02/15 04:10:53
As coded, this will result in many small transacti
benm (inactive)
2012/02/15 17:38:17
Done.
|
| + FROM_HERE, base::Bind(&DomStorageArea::CommitChanges, this))) { |
| + // If we know that the task will not run, try and save changes synchronously |
| + // so that we don't lose data. |
| + CommitChanges(); |
| + } |
| +} |
| + |
| +void DomStorageArea::CommitChanges() { |
| + DCHECK(backing_.get()); |
| + if (backing_->CommitChanges(clear_all_next_commit_, changed_values_)) { |
| + clear_all_next_commit_ = false; |
| + changed_values_.clear(); |
| + } |
| +} |
| + |
| +// static |
| +FilePath DomStorageArea::DatabaseFileNameFromOrigin(const GURL& origin) { |
| + std::string filename = fileapi::GetOriginIdentifierFromURL(origin) |
| + + ".localstorage"; |
| + return FilePath().AppendASCII(filename); |
| +} |
| + |
| } // namespace dom_storage |