summaryrefslogtreecommitdiffstats
path: root/webkit/dom_storage
diff options
context:
space:
mode:
authormichaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-23 01:32:45 +0000
committermichaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-23 01:32:45 +0000
commit04917467b86a41966255581c6d63a75338d8e770 (patch)
tree92a19336778b082a80bade512362f4a52d19b756 /webkit/dom_storage
parent7b6c0ccdd71595ffc25542ee5de10f0eb555fc2d (diff)
downloadchromium_src-04917467b86a41966255581c6d63a75338d8e770.zip
chromium_src-04917467b86a41966255581c6d63a75338d8e770.tar.gz
chromium_src-04917467b86a41966255581c6d63a75338d8e770.tar.bz2
DomStorage data deletion and memory purging.
BUG=106763 Review URL: https://chromiumcodereview.appspot.com/9817011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@128376 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/dom_storage')
-rw-r--r--webkit/dom_storage/dom_storage_area.cc58
-rw-r--r--webkit/dom_storage/dom_storage_area.h14
-rw-r--r--webkit/dom_storage/dom_storage_area_unittest.cc170
-rw-r--r--webkit/dom_storage/dom_storage_context.cc92
-rw-r--r--webkit/dom_storage/dom_storage_context.h4
-rw-r--r--webkit/dom_storage/dom_storage_database.cc28
-rw-r--r--webkit/dom_storage/dom_storage_database.h8
-rw-r--r--webkit/dom_storage/dom_storage_database_unittest.cc1
-rw-r--r--webkit/dom_storage/dom_storage_namespace.cc36
-rw-r--r--webkit/dom_storage/dom_storage_namespace.h1
10 files changed, 367 insertions, 45 deletions
diff --git a/webkit/dom_storage/dom_storage_area.cc b/webkit/dom_storage/dom_storage_area.cc
index 186a4f6..93946b8 100644
--- a/webkit/dom_storage/dom_storage_area.cc
+++ b/webkit/dom_storage/dom_storage_area.cc
@@ -5,14 +5,20 @@
#include "webkit/dom_storage/dom_storage_area.h"
#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/location.h"
#include "base/logging.h"
#include "base/time.h"
-#include "base/tracked_objects.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
+#include "webkit/database/database_util.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"
+#include "webkit/glue/webkit_glue.h"
+
+using webkit_database::DatabaseUtil;
namespace dom_storage {
@@ -38,6 +44,14 @@ FilePath DomStorageArea::DatabaseFileNameFromOrigin(const GURL& origin) {
InsertBeforeExtensionASCII(filename);
}
+// static
+GURL DomStorageArea::OriginFromDatabaseFileName(const FilePath& name) {
+ DCHECK(name.MatchesExtension(kDatabaseFileExtension));
+ WebKit::WebString origin_id = webkit_glue::FilePathToWebString(
+ name.BaseName().RemoveExtension());
+ return DatabaseUtil::GetOriginFromIdentifier(origin_id);
+}
+
DomStorageArea::DomStorageArea(
int64 namespace_id, const GURL& origin,
const FilePath& directory, DomStorageTaskRunner* task_runner)
@@ -138,6 +152,48 @@ DomStorageArea* DomStorageArea::ShallowCopy(int64 destination_namespace_id) {
return copy;
}
+bool DomStorageArea::HasUncommittedChanges() const {
+ DCHECK(!is_shutdown_);
+ return commit_batch_.get() || in_flight_commit_batch_.get();
+}
+
+void DomStorageArea::DeleteOrigin() {
+ DCHECK(!is_shutdown_);
+ if (HasUncommittedChanges()) {
+ // TODO(michaeln): This logically deletes the data immediately,
+ // and in a matter of a second, deletes the rows from the backing
+ // database file, but the file itself will linger until shutdown
+ // or purge time. Ideally, this should delete the file more
+ // quickly.
+ Clear();
+ return;
+ }
+ map_ = new DomStorageMap(kPerAreaQuota);
+ if (backing_.get()) {
+ is_initial_import_done_ = false;
+ backing_.reset(new DomStorageDatabase(backing_->file_path()));
+ file_util::Delete(backing_->file_path(), false);
+ file_util::Delete(
+ DomStorageDatabase::GetJournalFilePath(backing_->file_path()), false);
+ }
+}
+
+void DomStorageArea::PurgeMemory() {
+ DCHECK(!is_shutdown_);
+ if (!is_initial_import_done_ || // We're not using any memory.
+ !backing_.get() || // We can't purge anything.
+ HasUncommittedChanges()) // We leave things alone with changes pending.
+ return;
+
+ // Drop the in memory cache, we'll reload when needed.
+ is_initial_import_done_ = false;
+ map_ = new DomStorageMap(kPerAreaQuota);
+
+ // Recreate the database object, this frees up the open sqlite connection
+ // and its page cache.
+ backing_.reset(new DomStorageDatabase(backing_->file_path()));
+}
+
void DomStorageArea::Shutdown() {
DCHECK(!is_shutdown_);
is_shutdown_ = true;
diff --git a/webkit/dom_storage/dom_storage_area.h b/webkit/dom_storage/dom_storage_area.h
index 8d55a9b..0bad246 100644
--- a/webkit/dom_storage/dom_storage_area.h
+++ b/webkit/dom_storage/dom_storage_area.h
@@ -29,6 +29,7 @@ class DomStorageArea
public:
static const FilePath::CharType kDatabaseFileExtension[];
static FilePath DatabaseFileNameFromOrigin(const GURL& origin);
+ static GURL OriginFromDatabaseFileName(const FilePath& file_name);
DomStorageArea(int64 namespace_id,
const GURL& origin,
@@ -48,6 +49,17 @@ class DomStorageArea
DomStorageArea* ShallowCopy(int64 destination_namespace_id);
+ bool HasUncommittedChanges() const;
+
+ // Similar to Clear() but more optimized for just deleting
+ // without raising events.
+ void DeleteOrigin();
+
+ // Frees up memory when possible. Typically, this method returns
+ // the object to its just constructed state, however if uncommitted
+ // changes are pending, it does nothing.
+ void PurgeMemory();
+
// Schedules the commit of any unsaved changes and enters a
// shutdown state such that the value getters and setters will
// no longer do anything.
@@ -60,6 +72,8 @@ class DomStorageArea
FRIEND_TEST_ALL_PREFIXES(DomStorageAreaTest, TestDatabaseFilePath);
FRIEND_TEST_ALL_PREFIXES(DomStorageAreaTest, CommitTasks);
FRIEND_TEST_ALL_PREFIXES(DomStorageAreaTest, CommitChangesAtShutdown);
+ FRIEND_TEST_ALL_PREFIXES(DomStorageAreaTest, DeleteOrigin);
+ FRIEND_TEST_ALL_PREFIXES(DomStorageAreaTest, PurgeMemory);
friend class base::RefCountedThreadSafe<DomStorageArea>;
struct CommitBatch {
diff --git a/webkit/dom_storage/dom_storage_area_unittest.cc b/webkit/dom_storage/dom_storage_area_unittest.cc
index 10c23be..12ccfd8 100644
--- a/webkit/dom_storage/dom_storage_area_unittest.cc
+++ b/webkit/dom_storage/dom_storage_area_unittest.cc
@@ -40,12 +40,14 @@ class DomStorageAreaTest : public testing::Test {
// Verify that it put a commit in flight.
EXPECT_TRUE(area->in_flight_commit_batch_.get());
EXPECT_FALSE(area->commit_batch_.get());
+ EXPECT_TRUE(area->HasUncommittedChanges());
// Make additional change and verify that a new commit batch
// is created for that change.
NullableString16 old_value;
EXPECT_TRUE(area->SetItem(kKey2, kValue2, &old_value));
EXPECT_TRUE(area->commit_batch_.get());
EXPECT_TRUE(area->in_flight_commit_batch_.get());
+ EXPECT_TRUE(area->HasUncommittedChanges());
}
// Class used in the CommitChangesAtShutdown test case.
@@ -77,6 +79,7 @@ TEST_F(DomStorageAreaTest, DomStorageAreaBasics) {
EXPECT_EQ(0u, area->Length());
EXPECT_TRUE(area->SetItem(kKey, kValue, &old_nullable_value));
EXPECT_TRUE(area->SetItem(kKey2, kValue2, &old_nullable_value));
+ EXPECT_FALSE(area->HasUncommittedChanges());
// Verify that a copy shares the same map.
copy = area->ShallowCopy(2);
@@ -212,6 +215,7 @@ TEST_F(DomStorageAreaTest, CommitTasks) {
// See that changes are batched up.
EXPECT_FALSE(area->commit_batch_.get());
EXPECT_TRUE(area->SetItem(kKey, kValue, &old_value));
+ EXPECT_TRUE(area->HasUncommittedChanges());
EXPECT_TRUE(area->commit_batch_.get());
EXPECT_FALSE(area->commit_batch_->clear_all_first);
EXPECT_EQ(1u, area->commit_batch_->changed_values.size());
@@ -220,6 +224,7 @@ TEST_F(DomStorageAreaTest, CommitTasks) {
EXPECT_FALSE(area->commit_batch_->clear_all_first);
EXPECT_EQ(2u, area->commit_batch_->changed_values.size());
MessageLoop::current()->RunAllPending();
+ EXPECT_FALSE(area->HasUncommittedChanges());
EXPECT_FALSE(area->commit_batch_.get());
EXPECT_FALSE(area->in_flight_commit_batch_.get());
// Verify the changes made it to the database.
@@ -245,7 +250,7 @@ TEST_F(DomStorageAreaTest, CommitTasks) {
// See that if changes accrue while a commit is "in flight"
// those will also get committed.
EXPECT_TRUE(area->SetItem(kKey, kValue, &old_value));
- EXPECT_TRUE(area->commit_batch_.get());
+ EXPECT_TRUE(area->HasUncommittedChanges());
// At this point the OnCommitTimer task has been posted. We inject
// another task in the queue that will execute after the timer task,
// but before the CommitChanges task. From within our injected task,
@@ -256,8 +261,7 @@ TEST_F(DomStorageAreaTest, CommitTasks) {
base::Unretained(this), area));
MessageLoop::current()->RunAllPending();
EXPECT_TRUE(area->HasOneRef());
- EXPECT_FALSE(area->commit_batch_.get());
- EXPECT_FALSE(area->in_flight_commit_batch_.get());
+ EXPECT_FALSE(area->HasUncommittedChanges());
// Verify the changes made it to the database.
values.clear();
area->backing_->ReadAllValues(&values);
@@ -281,7 +285,7 @@ TEST_F(DomStorageAreaTest, CommitChangesAtShutdown) {
ValuesMap values;
NullableString16 old_value;
EXPECT_TRUE(area->SetItem(kKey, kValue, &old_value));
- EXPECT_TRUE(area->commit_batch_.get());
+ EXPECT_TRUE(area->HasUncommittedChanges());
area->backing_->ReadAllValues(&values);
EXPECT_TRUE(values.empty()); // not committed yet
area->Shutdown();
@@ -292,18 +296,156 @@ TEST_F(DomStorageAreaTest, CommitChangesAtShutdown) {
// were committed.
}
-TEST_F(DomStorageAreaTest, TestDatabaseFilePath) {
- EXPECT_EQ(FilePath().AppendASCII("file_path_to_0.localstorage"),
- DomStorageArea::DatabaseFileNameFromOrigin(
- GURL("file://path_to/index.html")));
+TEST_F(DomStorageAreaTest, DeleteOrigin) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ scoped_refptr<DomStorageArea> area(
+ new DomStorageArea(kLocalStorageNamespaceId, kOrigin,
+ temp_dir.path(),
+ new MockDomStorageTaskRunner(base::MessageLoopProxy::current())));
+
+ // This test puts files on disk.
+ FilePath db_file_path = area->backing_->file_path();
+ FilePath db_journal_file_path =
+ DomStorageDatabase::GetJournalFilePath(db_file_path);
+
+ // Nothing bad should happen when invoked w/o any files on disk.
+ area->DeleteOrigin();
+ EXPECT_FALSE(file_util::PathExists(db_file_path));
+
+ // Commit something in the database and then delete.
+ NullableString16 old_value;
+ area->SetItem(kKey, kValue, &old_value);
+ MessageLoop::current()->RunAllPending();
+ EXPECT_TRUE(file_util::PathExists(db_file_path));
+ area->DeleteOrigin();
+ EXPECT_EQ(0u, area->Length());
+ EXPECT_FALSE(file_util::PathExists(db_file_path));
+ EXPECT_FALSE(file_util::PathExists(db_journal_file_path));
+
+ // Put some uncommitted changes to a non-existing database in
+ // and then delete. No file ever gets created in this case.
+ area->SetItem(kKey, kValue, &old_value);
+ EXPECT_TRUE(area->HasUncommittedChanges());
+ EXPECT_EQ(1u, area->Length());
+ area->DeleteOrigin();
+ EXPECT_TRUE(area->HasUncommittedChanges());
+ EXPECT_EQ(0u, area->Length());
+ MessageLoop::current()->RunAllPending();
+ EXPECT_FALSE(area->HasUncommittedChanges());
+ EXPECT_FALSE(file_util::PathExists(db_file_path));
- EXPECT_EQ(FilePath().AppendASCII("https_www.google.com_0.localstorage"),
- DomStorageArea::DatabaseFileNameFromOrigin(
- GURL("https://www.google.com/")));
+ // Put some uncommitted changes to a an existing database in
+ // and then delete.
+ area->SetItem(kKey, kValue, &old_value);
+ MessageLoop::current()->RunAllPending();
+ EXPECT_TRUE(file_util::PathExists(db_file_path));
+ area->SetItem(kKey2, kValue2, &old_value);
+ EXPECT_TRUE(area->HasUncommittedChanges());
+ EXPECT_EQ(2u, area->Length());
+ area->DeleteOrigin();
+ EXPECT_TRUE(area->HasUncommittedChanges());
+ EXPECT_EQ(0u, area->Length());
+ MessageLoop::current()->RunAllPending();
+ EXPECT_FALSE(area->HasUncommittedChanges());
+ // Since the area had uncommitted changes at the time delete
+ // was called, the file will linger until the shutdown time.
+ EXPECT_TRUE(file_util::PathExists(db_file_path));
+ area->Shutdown();
+ MessageLoop::current()->RunAllPending();
+ EXPECT_FALSE(file_util::PathExists(db_file_path));
+}
+
+TEST_F(DomStorageAreaTest, PurgeMemory) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ scoped_refptr<DomStorageArea> area(
+ new DomStorageArea(kLocalStorageNamespaceId, kOrigin,
+ temp_dir.path(),
+ new MockDomStorageTaskRunner(base::MessageLoopProxy::current())));
+
+ // Inject an in-memory db to speed up the test.
+ area->backing_.reset(new DomStorageDatabase());
+
+ // Unowned ptrs we use to verify that 'purge' has happened.
+ DomStorageDatabase* original_backing = area->backing_.get();
+ DomStorageMap* original_map = area->map_.get();
+
+ // Should do no harm when called on a newly constructed object.
+ EXPECT_FALSE(area->is_initial_import_done_);
+ area->PurgeMemory();
+ EXPECT_FALSE(area->is_initial_import_done_);
+ EXPECT_EQ(original_backing, area->backing_.get());
+ EXPECT_EQ(original_map, area->map_.get());
+
+ // Should not do anything when commits are pending.
+ NullableString16 old_value;
+ area->SetItem(kKey, kValue, &old_value);
+ EXPECT_TRUE(area->is_initial_import_done_);
+ EXPECT_TRUE(area->HasUncommittedChanges());
+ area->PurgeMemory();
+ EXPECT_TRUE(area->is_initial_import_done_);
+ EXPECT_TRUE(area->HasUncommittedChanges());
+ EXPECT_EQ(original_backing, area->backing_.get());
+ EXPECT_EQ(original_map, area->map_.get());
+
+ // Commit the changes from above,
+ MessageLoop::current()->RunAllPending();
+ EXPECT_FALSE(area->HasUncommittedChanges());
+ EXPECT_EQ(original_backing, area->backing_.get());
+ EXPECT_EQ(original_map, area->map_.get());
+
+ // Should drop caches and reset database connections
+ // when invoked on an area that's loaded up primed.
+ area->PurgeMemory();
+ EXPECT_FALSE(area->is_initial_import_done_);
+ EXPECT_NE(original_backing, area->backing_.get());
+ EXPECT_NE(original_map, area->map_.get());
+}
+
+TEST_F(DomStorageAreaTest, DatabaseFileNames) {
+ struct {
+ const char* origin;
+ const char* file_name;
+ const char* journal_file_name;
+ } kCases[] = {
+ { "https://www.google.com/",
+ "https_www.google.com_0.localstorage",
+ "https_www.google.com_0.localstorage-journal" },
+ { "http://www.google.com:8080/",
+ "http_www.google.com_8080.localstorage",
+ "http_www.google.com_8080.localstorage-journal" },
+ { "file:///",
+ "file__0.localstorage",
+ "file__0.localstorage-journal" },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kCases); ++i) {
+ GURL origin = GURL(kCases[i].origin).GetOrigin();
+ FilePath file_name = FilePath().AppendASCII(kCases[i].file_name);
+ FilePath journal_file_name =
+ FilePath().AppendASCII(kCases[i].journal_file_name);
+
+ EXPECT_EQ(file_name,
+ DomStorageArea::DatabaseFileNameFromOrigin(origin));
+ EXPECT_EQ(origin,
+ DomStorageArea::OriginFromDatabaseFileName(file_name));
+ EXPECT_EQ(journal_file_name,
+ DomStorageDatabase::GetJournalFilePath(file_name));
+ }
- EXPECT_EQ(FilePath().AppendASCII("https_www.google.com_8080.localstorage"),
- DomStorageArea::DatabaseFileNameFromOrigin(
- GURL("https://www.google.com:8080")));
+ // Also test some DomStorageDatabase::GetJournalFilePath cases here.
+ FilePath parent = FilePath().AppendASCII("a").AppendASCII("b");
+ EXPECT_EQ(
+ parent.AppendASCII("file-journal"),
+ DomStorageDatabase::GetJournalFilePath(parent.AppendASCII("file")));
+ EXPECT_EQ(
+ FilePath().AppendASCII("-journal"),
+ DomStorageDatabase::GetJournalFilePath(FilePath()));
+ EXPECT_EQ(
+ FilePath().AppendASCII(".extensiononly-journal"),
+ DomStorageDatabase::GetJournalFilePath(
+ FilePath().AppendASCII(".extensiononly")));
}
} // namespace dom_storage
diff --git a/webkit/dom_storage/dom_storage_context.cc b/webkit/dom_storage/dom_storage_context.cc
index 635da33..5179a0e 100644
--- a/webkit/dom_storage/dom_storage_context.cc
+++ b/webkit/dom_storage/dom_storage_context.cc
@@ -7,21 +7,19 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/file_util.h"
+#include "base/location.h"
#include "base/time.h"
-#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
-#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
#include "webkit/dom_storage/dom_storage_area.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/glue/webkit_glue.h"
#include "webkit/quota/special_storage_policy.h"
using file_util::FileEnumerator;
namespace dom_storage {
-DomStorageContext::UsageInfo::UsageInfo() {}
+DomStorageContext::UsageInfo::UsageInfo() : data_size(0) {}
DomStorageContext::UsageInfo::~UsageInfo() {}
DomStorageContext::DomStorageContext(
@@ -60,35 +58,45 @@ DomStorageNamespace* DomStorageContext::GetStorageNamespace(
return found->second;
}
-void DomStorageContext::GetUsageInfo(std::vector<UsageInfo>* infos) {
+void DomStorageContext::GetUsageInfo(std::vector<UsageInfo>* infos,
+ bool include_file_info) {
+ if (directory_.empty())
+ return;
FileEnumerator enumerator(directory_, false, FileEnumerator::FILES);
for (FilePath path = enumerator.Next(); !path.empty();
path = enumerator.Next()) {
if (path.MatchesExtension(DomStorageArea::kDatabaseFileExtension)) {
UsageInfo info;
-
- // Extract origin id from the path and construct a GURL.
- WebKit::WebString origin_id = webkit_glue::FilePathToWebString(
- path.BaseName().RemoveExtension());
- info.origin = GURL(WebKit::WebSecurityOrigin::
- createFromDatabaseIdentifier(origin_id).toString());
-
- FileEnumerator::FindInfo find_info;
- enumerator.GetFindInfo(&find_info);
- info.data_size = FileEnumerator::GetFilesize(find_info);
- info.last_modified = FileEnumerator::GetLastModifiedTime(find_info);
-
+ info.origin = DomStorageArea::OriginFromDatabaseFileName(path);
+ if (include_file_info) {
+ FileEnumerator::FindInfo find_info;
+ enumerator.GetFindInfo(&find_info);
+ info.data_size = FileEnumerator::GetFilesize(find_info);
+ info.last_modified = FileEnumerator::GetLastModifiedTime(find_info);
+ }
infos->push_back(info);
}
}
}
void DomStorageContext::DeleteOrigin(const GURL& origin) {
- // TODO(michaeln): write me
+ DCHECK(!is_shutdown_);
+ DomStorageNamespace* local = GetStorageNamespace(kLocalStorageNamespaceId);
+ local->DeleteOrigin(origin);
}
void DomStorageContext::DeleteDataModifiedSince(const base::Time& cutoff) {
- // TODO(michaeln): write me
+ std::vector<UsageInfo> infos;
+ const bool kIncludeFileInfo = true;
+ GetUsageInfo(&infos, kIncludeFileInfo);
+ for (size_t i = 0; i < infos.size(); ++i) {
+ if (infos[i].last_modified > cutoff) {
+ if (!special_storage_policy_ ||
+ !special_storage_policy_->IsStorageProtected(infos[i].origin)) {
+ DeleteOrigin(infos[i].origin);
+ }
+ }
+ }
}
void DomStorageContext::PurgeMemory() {
@@ -105,6 +113,29 @@ void DomStorageContext::Shutdown() {
StorageNamespaceMap::const_iterator it = namespaces_.begin();
for (; it != namespaces_.end(); ++it)
it->second->Shutdown();
+
+ if (directory_.empty())
+ return;
+
+ // Respect the content policy settings about what to
+ // keep and what to discard.
+ if (save_session_state_)
+ return; // Keep everything.
+
+ bool has_session_only_origins =
+ special_storage_policy_.get() &&
+ special_storage_policy_->HasSessionOnlyOrigins();
+
+ if (clear_local_state_ || has_session_only_origins) {
+ // We may have to delete something. We continue on the
+ // commit sequence after area shutdown tasks have cycled
+ // thru that sequence (and closed their database files).
+ bool success = task_runner_->PostShutdownBlockingTask(
+ FROM_HERE,
+ DomStorageTaskRunner::COMMIT_SEQUENCE,
+ base::Bind(&DomStorageContext::ClearLocalStateInCommitSequence, this));
+ DCHECK(success);
+ }
}
void DomStorageContext::AddEventObserver(EventObserver* observer) {
@@ -173,4 +204,27 @@ void DomStorageContext::CloneSessionNamespace(
CreateSessionNamespace(new_id);
}
+void DomStorageContext::ClearLocalStateInCommitSequence() {
+ std::vector<UsageInfo> infos;
+ const bool kDontIncludeFileInfo = false;
+ GetUsageInfo(&infos, kDontIncludeFileInfo);
+ for (size_t i = 0; i < infos.size(); ++i) {
+ const GURL& origin = infos[i].origin;
+ if (special_storage_policy_ &&
+ special_storage_policy_->IsStorageProtected(origin))
+ continue;
+ if (!clear_local_state_ &&
+ !special_storage_policy_->IsStorageSessionOnly(origin))
+ continue;
+
+ const bool kNotRecursive = false;
+ FilePath database_file_path = directory_.Append(
+ DomStorageArea::DatabaseFileNameFromOrigin(origin));
+ file_util::Delete(database_file_path, kNotRecursive);
+ file_util::Delete(
+ DomStorageDatabase::GetJournalFilePath(database_file_path),
+ kNotRecursive);
+ }
+}
+
} // namespace dom_storage
diff --git a/webkit/dom_storage/dom_storage_context.h b/webkit/dom_storage/dom_storage_context.h
index 2c44bc1..696f69e 100644
--- a/webkit/dom_storage/dom_storage_context.h
+++ b/webkit/dom_storage/dom_storage_context.h
@@ -94,7 +94,7 @@ class DomStorageContext
DomStorageTaskRunner* task_runner() const { return task_runner_; }
DomStorageNamespace* GetStorageNamespace(int64 namespace_id);
- void GetUsageInfo(std::vector<UsageInfo>* info);
+ void GetUsageInfo(std::vector<UsageInfo>* infos, bool include_file_info);
void DeleteOrigin(const GURL& origin);
void DeleteDataModifiedSince(const base::Time& cutoff);
void PurgeMemory();
@@ -152,6 +152,8 @@ class DomStorageContext
~DomStorageContext();
+ void ClearLocalStateInCommitSequence();
+
// Collection of namespaces keyed by id.
StorageNamespaceMap namespaces_;
diff --git a/webkit/dom_storage/dom_storage_database.cc b/webkit/dom_storage/dom_storage_database.cc
index 93c0060..97fd3da 100644
--- a/webkit/dom_storage/dom_storage_database.cc
+++ b/webkit/dom_storage/dom_storage_database.cc
@@ -13,6 +13,8 @@
namespace {
+const FilePath::CharType kJournal[] = FILE_PATH_LITERAL("-journal");
+
class HistogramUniquifier {
public:
static const char* name() { return "Sqlite.DomStorageDatabase.Error"; }
@@ -26,14 +28,19 @@ sql::ErrorDelegate* GetErrorHandlerForDomStorageDatabase() {
namespace dom_storage {
+// static
+FilePath DomStorageDatabase::GetJournalFilePath(
+ const FilePath& database_path) {
+ FilePath::StringType journal_file_name =
+ database_path.BaseName().value() + kJournal;
+ return database_path.DirName().Append(journal_file_name);
+}
+
DomStorageDatabase::DomStorageDatabase(const FilePath& file_path)
: file_path_(file_path) {
// Note: in normal use we should never get an empty backing path here.
- // However, the unit test for this class defines another constructor
- // that will bypass this check to allow an empty path that signifies
- // we should operate on an in-memory database for performance/reliability
- // reasons.
- DCHECK(!file_path_.empty());
+ // However, the unit test for this class can contruct an instance
+ // with an empty path.
Init();
}
@@ -49,9 +56,10 @@ void DomStorageDatabase::Init() {
DomStorageDatabase::~DomStorageDatabase() {
if (known_to_be_empty_ && !file_path_.empty()) {
- // Delete the db from disk, it's empty.
+ // Delete the db and any lingering journal file from disk.
Close();
file_util::Delete(file_path_, false);
+ file_util::Delete(GetJournalFilePath(file_path_), false);
}
}
@@ -74,8 +82,12 @@ void DomStorageDatabase::ReadAllValues(ValuesMap* result) {
bool DomStorageDatabase::CommitChanges(bool clear_all_first,
const ValuesMap& changes) {
- if (!LazyOpen(!changes.empty()))
- return false;
+ if (!LazyOpen(!changes.empty())) {
+ // If we're being asked to commit changes that will result in an
+ // empty database, we return true if the database file doesn't exist.
+ return clear_all_first && changes.empty() &&
+ !file_util::PathExists(file_path_);
+ }
bool old_known_to_be_empty = known_to_be_empty_;
sql::Transaction transaction(db_.get());
diff --git a/webkit/dom_storage/dom_storage_database.h b/webkit/dom_storage/dom_storage_database.h
index 6bd4dfa..5d0d9ed 100644
--- a/webkit/dom_storage/dom_storage_database.h
+++ b/webkit/dom_storage/dom_storage_database.h
@@ -22,6 +22,8 @@ namespace dom_storage {
// class is designed to be used on a single thread.
class DomStorageDatabase {
public:
+ static FilePath GetJournalFilePath(const FilePath& database_path);
+
explicit DomStorageDatabase(const FilePath& file_path);
virtual ~DomStorageDatabase(); // virtual for unit testing
@@ -38,6 +40,9 @@ class DomStorageDatabase {
// will be removed and all others will be inserted/updated as appropriate.
bool CommitChanges(bool clear_all_first, const ValuesMap& changes);
+ // Simple getter for the path we were constructed with.
+ const FilePath& file_path() const { return file_path_; }
+
protected:
// Constructor that uses an in-memory sqlite database, for testing.
DomStorageDatabase();
@@ -59,6 +64,7 @@ class DomStorageDatabase {
TestCanOpenFileThatIsNotADatabase);
FRIEND_TEST_ALL_PREFIXES(DomStorageAreaTest, BackingDatabaseOpened);
FRIEND_TEST_ALL_PREFIXES(DomStorageAreaTest, CommitTasks);
+ FRIEND_TEST_ALL_PREFIXES(DomStorageAreaTest, PurgeMemory);
enum SchemaVersion {
INVALID,
@@ -100,7 +106,7 @@ class DomStorageDatabase {
void Init();
// Path to the database on disk.
- FilePath file_path_;
+ const FilePath file_path_;
scoped_ptr<sql::Connection> db_;
bool failed_to_open_;
bool tried_to_recreate_;
diff --git a/webkit/dom_storage/dom_storage_database_unittest.cc b/webkit/dom_storage/dom_storage_database_unittest.cc
index 0757137..ddf2a69 100644
--- a/webkit/dom_storage/dom_storage_database_unittest.cc
+++ b/webkit/dom_storage/dom_storage_database_unittest.cc
@@ -118,6 +118,7 @@ TEST(DomStorageDatabaseTest, CloseEmptyDatabaseDeletesFile) {
// trigger it's deletion from disk.
{
DomStorageDatabase db(file_name);
+ EXPECT_EQ(file_name, db.file_path());
ASSERT_TRUE(db.CommitChanges(false, storage));
}
EXPECT_TRUE(file_util::PathExists(file_name));
diff --git a/webkit/dom_storage/dom_storage_namespace.cc b/webkit/dom_storage/dom_storage_namespace.cc
index 7ea7f8d..656d244 100644
--- a/webkit/dom_storage/dom_storage_namespace.cc
+++ b/webkit/dom_storage/dom_storage_namespace.cc
@@ -64,8 +64,42 @@ DomStorageNamespace* DomStorageNamespace::Clone(int64 clone_namespace_id) {
return clone;
}
+void DomStorageNamespace::DeleteOrigin(const GURL& origin) {
+ AreaHolder* holder = GetAreaHolder(origin);
+ if (holder) {
+ holder->area_->DeleteOrigin();
+ return;
+ }
+ if (!directory_.empty()) {
+ scoped_refptr<DomStorageArea> area =
+ new DomStorageArea(namespace_id_, origin, directory_, task_runner_);
+ area->DeleteOrigin();
+ }
+}
+
void DomStorageNamespace::PurgeMemory() {
- // TODO(michaeln): write me
+ if (directory_.empty())
+ return; // We can't purge w/o backing on disk.
+ AreaMap::iterator it = areas_.begin();
+ while (it != areas_.end()) {
+ // Leave it alone if changes are pending
+ if (it->second.area_->HasUncommittedChanges()) {
+ ++it;
+ continue;
+ }
+
+ // If not in use, we can shut it down and remove
+ // it from our collection entirely.
+ if (it->second.open_count_ == 0) {
+ it->second.area_->Shutdown();
+ areas_.erase(it++);
+ continue;
+ }
+
+ // Otherwise, we can clear caches and such.
+ it->second.area_->PurgeMemory();
+ ++it;
+ }
}
void DomStorageNamespace::Shutdown() {
diff --git a/webkit/dom_storage/dom_storage_namespace.h b/webkit/dom_storage/dom_storage_namespace.h
index a59b7b0..6063266 100644
--- a/webkit/dom_storage/dom_storage_namespace.h
+++ b/webkit/dom_storage/dom_storage_namespace.h
@@ -47,6 +47,7 @@ class DomStorageNamespace
// Should only be called for session storage namespaces.
DomStorageNamespace* Clone(int64 clone_namespace_id);
+ void DeleteOrigin(const GURL& origin);
void PurgeMemory();
void Shutdown();