| 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 e394f64db22b35b3eb2d320417194dd1b2df34c2..3eb195c459f156a4e22256213d3c04f9a275902b 100644
|
| --- a/webkit/dom_storage/dom_storage_area.cc
|
| +++ b/webkit/dom_storage/dom_storage_area.cc
|
| @@ -3,57 +3,113 @@
|
| // found in the LICENSE file.
|
|
|
| #include "webkit/dom_storage/dom_storage_area.h"
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/time.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/dom_storage/dom_storage_types.h"
|
| +#include "webkit/fileapi/file_system_util.h"
|
|
|
| namespace dom_storage {
|
|
|
| DomStorageArea::DomStorageArea(
|
| int64 namespace_id, const GURL& origin,
|
| const FilePath& directory, DomStorageTaskRunner* task_runner)
|
| - : namespace_id_(namespace_id),
|
| - origin_(origin),
|
| + : namespace_id_(namespace_id), origin_(origin),
|
| directory_(directory),
|
| task_runner_(task_runner),
|
| - map_(new DomStorageMap(kPerAreaQuota)) {
|
| + map_(new DomStorageMap(kPerAreaQuota)),
|
| + backing_(NULL),
|
| + initial_import_done_(false),
|
| + clear_all_next_commit_(false),
|
| + commit_in_flight_(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.
|
| + // We do this regardless of whether we think a commit is in flight
|
| + // as there is no guarantee that that commit will actually get
|
| + // processed. For example the task_runner_'s message loop could
|
| + // unexpectedly quit before the delayed task is fired and leave the
|
| + // commit_in_flight_ flag set. But there's no way for us to determine
|
| + // that has happened so force a commit now.
|
| +
|
| + CommitChanges();
|
| +
|
| + // TODO(benm): It's possible that the commit failed, and in
|
| + // that case we're going to lose data. Integrate with UMA
|
| + // to gather stats about how often this actually happens,
|
| + // so that we can figure out a contingency plan.
|
| + }
|
| }
|
|
|
| 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(kPerAreaQuota);
|
| +
|
| + if (backing_.get()) {
|
| + changed_values_.clear();
|
| + clear_all_next_commit_ = true;
|
| + ScheduleCommitChanges();
|
| + }
|
| +
|
| return true;
|
| }
|
|
|
| @@ -61,10 +117,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());
|
| + DCHECK(clear_all_next_commit_ || !changed_values_.empty());
|
| + DCHECK(task_runner_.get());
|
| +
|
| + if (commit_in_flight_)
|
| + return;
|
| +
|
| + commit_in_flight_ = task_runner_->PostDelayedTask(
|
| + FROM_HERE, base::Bind(&DomStorageArea::CommitChanges, this),
|
| + base::TimeDelta::FromSeconds(1));
|
| + DCHECK(commit_in_flight_);
|
| +}
|
| +
|
| +void DomStorageArea::CommitChanges() {
|
| + DCHECK(backing_.get());
|
| + if (backing_->CommitChanges(clear_all_next_commit_, changed_values_)) {
|
| + clear_all_next_commit_ = false;
|
| + changed_values_.clear();
|
| + }
|
| + commit_in_flight_ = false;
|
| +}
|
| +
|
| +// static
|
| +FilePath DomStorageArea::DatabaseFileNameFromOrigin(const GURL& origin) {
|
| + std::string filename = fileapi::GetOriginIdentifierFromURL(origin)
|
| + + ".localstorage";
|
| + return FilePath().AppendASCII(filename);
|
| +}
|
| +
|
| } // namespace dom_storage
|
|
|