summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorericu@chromium.org <ericu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-03 16:26:19 +0000
committerericu@chromium.org <ericu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-03 16:26:19 +0000
commit3cf0cae325bf82ed3b5523b4696d8899b57e8774 (patch)
treea3db6339b6727ee3cdfac3b35169e3ef48875789
parent174e3bab30cc18b9b7956b5c423f4f0610c68bde (diff)
downloadchromium_src-3cf0cae325bf82ed3b5523b4696d8899b57e8774.zip
chromium_src-3cf0cae325bf82ed3b5523b4696d8899b57e8774.tar.gz
chromium_src-3cf0cae325bf82ed3b5523b4696d8899b57e8774.tar.bz2
Add the IndexedDBActiveBlobRegistry, currently unused, to enable future blob
support. BUG=108012 R=cmumford,jsbell Review URL: https://codereview.chromium.org/203833003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@261440 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--content/browser/indexed_db/indexed_db_active_blob_registry.cc148
-rw-r--r--content/browser/indexed_db/indexed_db_active_blob_registry.h74
-rw-r--r--content/browser/indexed_db/indexed_db_active_blob_registry_unittest.cc270
-rw-r--r--content/browser/indexed_db/indexed_db_backing_store.cc61
-rw-r--r--content/browser/indexed_db/indexed_db_backing_store.h44
-rw-r--r--content/browser/indexed_db/indexed_db_backing_store_unittest.cc3
-rw-r--r--content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc42
-rw-r--r--content/browser/indexed_db/indexed_db_factory.cc40
-rw-r--r--content/browser/indexed_db/indexed_db_factory.h5
-rw-r--r--content/browser/indexed_db/indexed_db_factory_unittest.cc196
-rw-r--r--content/browser/indexed_db/indexed_db_fake_backing_store.cc19
-rw-r--r--content/browser/indexed_db/indexed_db_fake_backing_store.h14
-rw-r--r--content/content_browser.gypi2
-rw-r--r--content/content_tests.gypi1
14 files changed, 785 insertions, 134 deletions
diff --git a/content/browser/indexed_db/indexed_db_active_blob_registry.cc b/content/browser/indexed_db/indexed_db_active_blob_registry.cc
new file mode 100644
index 0000000..3956c33
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_active_blob_registry.cc
@@ -0,0 +1,148 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/stl_util.h"
+#include "base/task_runner.h"
+#include "content/browser/indexed_db/indexed_db_active_blob_registry.h"
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_factory.h"
+#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
+
+namespace content {
+
+IndexedDBActiveBlobRegistry::IndexedDBActiveBlobRegistry(
+ IndexedDBBackingStore* backing_store)
+ : backing_store_(backing_store), weak_factory_(this) {}
+
+IndexedDBActiveBlobRegistry::~IndexedDBActiveBlobRegistry() {
+ DCHECK(use_tracker_.empty());
+}
+
+void IndexedDBActiveBlobRegistry::AddBlobRef(int64 database_id,
+ int64 blob_key) {
+ DCHECK(backing_store_);
+ DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
+ DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
+ DCHECK(!ContainsKey(deleted_dbs_, database_id));
+ bool need_ref = use_tracker_.empty();
+ SingleDBMap& single_db_map = use_tracker_[database_id];
+ SingleDBMap::iterator iter = single_db_map.find(blob_key);
+ if (iter == single_db_map.end()) {
+ single_db_map[blob_key] = false;
+ if (need_ref) {
+ backing_store_->factory()->ReportOutstandingBlobs(
+ backing_store_->origin_url(), true);
+ }
+ } else {
+ DCHECK(!need_ref);
+ DCHECK(!iter->second); // You can't add a reference once it's been deleted.
+ }
+}
+
+void IndexedDBActiveBlobRegistry::ReleaseBlobRef(int64 database_id,
+ int64 blob_key) {
+ DCHECK(backing_store_);
+ DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
+ DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
+ AllDBsMap::iterator db_pair = use_tracker_.find(database_id);
+ if (db_pair == use_tracker_.end()) {
+ NOTREACHED();
+ return;
+ }
+ SingleDBMap& single_db = db_pair->second;
+ SingleDBMap::iterator blob_pair = single_db.find(blob_key);
+ if (blob_pair == single_db.end()) {
+ NOTREACHED();
+ return;
+ }
+ bool delete_in_backend = false;
+ DeletedDBSet::iterator db_to_delete = deleted_dbs_.find(database_id);
+ bool db_marked_for_deletion = db_to_delete != deleted_dbs_.end();
+ // Don't bother deleting the file if we're going to delete its whole
+ // database directory soon.
+ delete_in_backend = blob_pair->second && !db_marked_for_deletion;
+ single_db.erase(blob_pair);
+ if (single_db.empty()) {
+ use_tracker_.erase(db_pair);
+ if (db_marked_for_deletion) {
+ delete_in_backend = true;
+ blob_key = DatabaseMetaDataKey::kAllBlobsKey;
+ deleted_dbs_.erase(db_to_delete);
+ }
+ }
+ if (delete_in_backend)
+ backing_store_->ReportBlobUnused(database_id, blob_key);
+ if (use_tracker_.empty()) {
+ backing_store_->factory()->ReportOutstandingBlobs(
+ backing_store_->origin_url(), false);
+ }
+}
+
+bool IndexedDBActiveBlobRegistry::MarkDeletedCheckIfUsed(int64 database_id,
+ int64 blob_key) {
+ DCHECK(backing_store_);
+ DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
+ DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
+ AllDBsMap::iterator db_pair = use_tracker_.find(database_id);
+ if (db_pair == use_tracker_.end())
+ return false;
+
+ if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
+ deleted_dbs_.insert(database_id);
+ return true;
+ }
+
+ SingleDBMap& single_db = db_pair->second;
+ SingleDBMap::iterator iter = single_db.find(blob_key);
+ if (iter == single_db.end())
+ return false;
+
+ iter->second = true;
+ return true;
+}
+
+void IndexedDBActiveBlobRegistry::ReleaseBlobRefThreadSafe(
+ scoped_refptr<base::TaskRunner> task_runner,
+ base::WeakPtr<IndexedDBActiveBlobRegistry> weak_ptr,
+ int64 database_id,
+ int64 blob_key,
+ const base::FilePath& unused) {
+ task_runner->PostTask(FROM_HERE,
+ base::Bind(&IndexedDBActiveBlobRegistry::ReleaseBlobRef,
+ weak_ptr,
+ database_id,
+ blob_key));
+}
+
+webkit_blob::ShareableFileReference::FinalReleaseCallback
+IndexedDBActiveBlobRegistry::GetFinalReleaseCallback(int64 database_id,
+ int64 blob_key) {
+ return base::Bind(
+ &IndexedDBActiveBlobRegistry::ReleaseBlobRefThreadSafe,
+ scoped_refptr<base::TaskRunner>(backing_store_->task_runner()),
+ weak_factory_.GetWeakPtr(),
+ database_id,
+ blob_key);
+}
+
+base::Closure IndexedDBActiveBlobRegistry::GetAddBlobRefCallback(
+ int64 database_id,
+ int64 blob_key) {
+ return base::Bind(&IndexedDBActiveBlobRegistry::AddBlobRef,
+ weak_factory_.GetWeakPtr(),
+ database_id,
+ blob_key);
+}
+
+void IndexedDBActiveBlobRegistry::ForceShutdown() {
+ weak_factory_.InvalidateWeakPtrs();
+ use_tracker_.clear();
+ backing_store_ = NULL;
+}
+
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_active_blob_registry.h b/content/browser/indexed_db/indexed_db_active_blob_registry.h
new file mode 100644
index 0000000..dffdda3
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_active_blob_registry.h
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_ACTIVE_BLOB_REGISTRY_H_
+#define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_ACTIVE_BLOB_REGISTRY_H_
+
+#include <map>
+#include <set>
+#include <utility>
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "content/common/content_export.h"
+#include "webkit/common/blob/shareable_file_reference.h"
+
+namespace content {
+
+class IndexedDBBackingStore;
+
+class CONTENT_EXPORT IndexedDBActiveBlobRegistry {
+ public:
+ IndexedDBActiveBlobRegistry(IndexedDBBackingStore* backing_store);
+ ~IndexedDBActiveBlobRegistry();
+
+ // Most methods of this class, and the closure returned by
+ // GetAddBlobRefCallback, should only be called on the backing_store's task
+ // runner. The exception is the closure returned by GetFinalReleaseCallback,
+ // which just calls ReleaseBlobRefThreadSafe.
+
+ // Use DatabaseMetaDataKey::AllBlobsKey for "the whole database".
+ bool MarkDeletedCheckIfUsed(int64 database_id, int64 blob_key);
+
+ webkit_blob::ShareableFileReference::FinalReleaseCallback
+ GetFinalReleaseCallback(int64 database_id, int64 blob_key);
+ // This closure holds a raw pointer to the IndexedDBActiveBlobRegistry,
+ // and may not be called after it is deleted.
+ base::Closure GetAddBlobRefCallback(int64 database_id, int64 blob_key);
+ // Call this to force the registry to drop its use counts, permitting the
+ // factory to drop any blob-related refcount for the backing store.
+ // This will also turn any outstanding callbacks into no-ops.
+ void ForceShutdown();
+
+ private:
+ void AddBlobRef(int64 database_id, int64 blob_key);
+ void ReleaseBlobRef(int64 database_id, int64 blob_key);
+ static void ReleaseBlobRefThreadSafe(
+ scoped_refptr<base::TaskRunner> task_runner,
+ base::WeakPtr<IndexedDBActiveBlobRegistry> weak_ptr,
+ int64 database_id,
+ int64 blob_key,
+ const base::FilePath& unused);
+
+ // Maps blob_key -> IsDeleted; if the record's absent, it's not in active use
+ // and we don't care if it's deleted.
+ typedef std::map<int64, bool> SingleDBMap;
+ // Maps DB ID -> SingleDBMap
+ typedef std::map<int64, SingleDBMap> AllDBsMap;
+ typedef std::set<int64> DeletedDBSet;
+
+ AllDBsMap use_tracker_;
+ DeletedDBSet deleted_dbs_;
+ // As long as we've got blobs registered in use_tracker_,
+ // backing_store_->factory() will keep backing_store_ alive for us. And
+ // backing_store_ owns us, so we'll stay alive as long as we're needed.
+ IndexedDBBackingStore* backing_store_;
+ base::WeakPtrFactory<IndexedDBActiveBlobRegistry> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBActiveBlobRegistry);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_ACTIVE_BLOB_REGISTRY_H_
diff --git a/content/browser/indexed_db/indexed_db_active_blob_registry_unittest.cc b/content/browser/indexed_db/indexed_db_active_blob_registry_unittest.cc
new file mode 100644
index 0000000..5c42b6a
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_active_blob_registry_unittest.cc
@@ -0,0 +1,270 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <set>
+
+#include "base/test/test_simple_task_runner.h"
+#include "content/browser/indexed_db/indexed_db_active_blob_registry.h"
+#include "content/browser/indexed_db/indexed_db_backing_store.h"
+#include "content/browser/indexed_db/indexed_db_factory.h"
+#include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+namespace {
+
+class MockIDBFactory : public IndexedDBFactory {
+ public:
+ MockIDBFactory() : IndexedDBFactory(NULL), duplicate_calls_(false) {}
+
+ virtual void ReportOutstandingBlobs(const GURL& origin_url,
+ bool blobs_outstanding) OVERRIDE {
+ if (blobs_outstanding) {
+ if (origins_.count(origin_url)) {
+ duplicate_calls_ = true;
+ } else {
+ origins_.insert(origin_url);
+ }
+ } else {
+ if (!origins_.count(origin_url)) {
+ duplicate_calls_ = true;
+ } else {
+ origins_.erase(origin_url);
+ }
+ }
+ }
+
+ bool CheckNoOriginsInUse() const {
+ return !duplicate_calls_ && !origins_.size();
+ }
+ bool CheckSingleOriginInUse(const GURL& origin) const {
+ return !duplicate_calls_ && origins_.size() == 1 && origins_.count(origin);
+ }
+
+ protected:
+ virtual ~MockIDBFactory() {}
+
+ private:
+ std::set<GURL> origins_;
+ bool duplicate_calls_;
+};
+
+class MockIDBBackingStore : public IndexedDBFakeBackingStore {
+ public:
+ MockIDBBackingStore(IndexedDBFactory* factory, base::TaskRunner* task_runner)
+ : IndexedDBFakeBackingStore(factory, task_runner),
+ duplicate_calls_(false) {}
+
+ virtual void ReportBlobUnused(int64 database_id, int64 blob_key) OVERRIDE {
+ unused_blobs_.insert(std::make_pair(database_id, blob_key));
+ }
+
+ typedef std::pair<int64, int64> KeyPair;
+ typedef std::set<KeyPair> KeyPairSet;
+ bool CheckUnusedBlobsEmpty() const {
+ return !duplicate_calls_ && !unused_blobs_.size();
+ }
+ bool CheckSingleUnusedBlob(int64 database_id, int64 blob_key) const {
+ return !duplicate_calls_ && unused_blobs_.size() == 1 &&
+ unused_blobs_.count(std::make_pair(database_id, blob_key));
+ }
+
+ const KeyPairSet& unused_blobs() const { return unused_blobs_; }
+
+ protected:
+ virtual ~MockIDBBackingStore() {}
+
+ private:
+ KeyPairSet unused_blobs_;
+ bool duplicate_calls_;
+};
+
+// Base class for our test fixtures.
+class IndexedDBActiveBlobRegistryTest : public testing::Test {
+ public:
+ IndexedDBActiveBlobRegistryTest()
+ : task_runner_(new base::TestSimpleTaskRunner),
+ factory_(new MockIDBFactory),
+ backing_store_(new MockIDBBackingStore(factory_, task_runner_)),
+ registry_(new IndexedDBActiveBlobRegistry(backing_store_.get())) {}
+
+ void RunUntilIdle() { task_runner_->RunUntilIdle(); }
+ MockIDBFactory* factory() const { return factory_.get(); }
+ MockIDBBackingStore* backing_store() const { return backing_store_.get(); }
+ IndexedDBActiveBlobRegistry* registry() const { return registry_.get(); }
+
+ static const int64 kDatabaseId0 = 7;
+ static const int64 kDatabaseId1 = 12;
+ static const int64 kBlobKey0 = 77;
+ static const int64 kBlobKey1 = 14;
+
+ typedef webkit_blob::ShareableFileReference::FinalReleaseCallback
+ ReleaseCallback;
+
+ private:
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ scoped_refptr<MockIDBFactory> factory_;
+ scoped_refptr<MockIDBBackingStore> backing_store_;
+ scoped_ptr<IndexedDBActiveBlobRegistry> registry_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBActiveBlobRegistryTest);
+};
+
+TEST_F(IndexedDBActiveBlobRegistryTest, DeleteUnused) {
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ EXPECT_FALSE(registry()->MarkDeletedCheckIfUsed(kDatabaseId0, kBlobKey0));
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+}
+
+TEST_F(IndexedDBActiveBlobRegistryTest, SimpleUse) {
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ base::Closure add_ref =
+ registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
+ ReleaseCallback release =
+ registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
+ add_ref.Run();
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ release.Run(base::FilePath());
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+}
+
+TEST_F(IndexedDBActiveBlobRegistryTest, DeleteWhileInUse) {
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ base::Closure add_ref =
+ registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
+ ReleaseCallback release =
+ registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
+
+ add_ref.Run();
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ EXPECT_TRUE(registry()->MarkDeletedCheckIfUsed(kDatabaseId0, kBlobKey0));
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ release.Run(base::FilePath());
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckSingleUnusedBlob(kDatabaseId0, kBlobKey0));
+}
+
+TEST_F(IndexedDBActiveBlobRegistryTest, MultipleBlobs) {
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ base::Closure add_ref_00 =
+ registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
+ ReleaseCallback release_00 =
+ registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
+ base::Closure add_ref_01 =
+ registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey1);
+ ReleaseCallback release_01 =
+ registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey1);
+ base::Closure add_ref_10 =
+ registry()->GetAddBlobRefCallback(kDatabaseId1, kBlobKey0);
+ ReleaseCallback release_10 =
+ registry()->GetFinalReleaseCallback(kDatabaseId1, kBlobKey0);
+ base::Closure add_ref_11 =
+ registry()->GetAddBlobRefCallback(kDatabaseId1, kBlobKey1);
+ ReleaseCallback release_11 =
+ registry()->GetFinalReleaseCallback(kDatabaseId1, kBlobKey1);
+
+ add_ref_00.Run();
+ add_ref_01.Run();
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ release_00.Run(base::FilePath());
+ add_ref_10.Run();
+ add_ref_11.Run();
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ EXPECT_TRUE(registry()->MarkDeletedCheckIfUsed(kDatabaseId0, kBlobKey1));
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ release_01.Run(base::FilePath());
+ release_11.Run(base::FilePath());
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckSingleUnusedBlob(kDatabaseId0, kBlobKey1));
+
+ release_10.Run(base::FilePath());
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckSingleUnusedBlob(kDatabaseId0, kBlobKey1));
+}
+
+TEST_F(IndexedDBActiveBlobRegistryTest, ForceShutdown) {
+ EXPECT_TRUE(factory()->CheckNoOriginsInUse());
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ base::Closure add_ref_0 =
+ registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
+ ReleaseCallback release_0 =
+ registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
+ base::Closure add_ref_1 =
+ registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey1);
+ ReleaseCallback release_1 =
+ registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey1);
+
+ add_ref_0.Run();
+ RunUntilIdle();
+
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ registry()->ForceShutdown();
+
+ add_ref_1.Run();
+ RunUntilIdle();
+
+ // Nothing changes.
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+
+ release_0.Run(base::FilePath());
+ release_1.Run(base::FilePath());
+ RunUntilIdle();
+
+ // Nothing changes.
+ EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
+ EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
+}
+
+} // namespace
+
+} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
index 089a03a..50ad609 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -422,13 +422,18 @@ class DefaultLevelDBFactory : public LevelDBFactory {
};
IndexedDBBackingStore::IndexedDBBackingStore(
+ IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
scoped_ptr<LevelDBDatabase> db,
- scoped_ptr<LevelDBComparator> comparator)
- : origin_url_(origin_url),
+ scoped_ptr<LevelDBComparator> comparator,
+ base::TaskRunner* task_runner)
+ : indexed_db_factory_(indexed_db_factory),
+ origin_url_(origin_url),
origin_identifier_(ComputeOriginIdentifier(origin_url)),
+ task_runner_(task_runner),
db_(db.Pass()),
- comparator_(comparator.Pass()) {}
+ comparator_(comparator.Pass()),
+ active_blob_registry_(this) {}
IndexedDBBackingStore::~IndexedDBBackingStore() {
// db_'s destructor uses comparator_. The order of destruction is important.
@@ -470,19 +475,23 @@ enum IndexedDBBackingStoreOpenResult {
// static
scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
+ IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
const base::FilePath& path_base,
blink::WebIDBDataLoss* data_loss,
std::string* data_loss_message,
- bool* disk_full) {
+ bool* disk_full,
+ base::TaskRunner* task_runner) {
*data_loss = blink::WebIDBDataLossNone;
DefaultLevelDBFactory leveldb_factory;
- return IndexedDBBackingStore::Open(origin_url,
+ return IndexedDBBackingStore::Open(indexed_db_factory,
+ origin_url,
path_base,
data_loss,
data_loss_message,
disk_full,
- &leveldb_factory);
+ &leveldb_factory,
+ task_runner);
}
static std::string OriginToCustomHistogramSuffix(const GURL& origin_url) {
@@ -628,12 +637,14 @@ bool IndexedDBBackingStore::RecordCorruptionInfo(
// static
scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
+ IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
const base::FilePath& path_base,
blink::WebIDBDataLoss* data_loss,
std::string* data_loss_message,
bool* is_disk_full,
- LevelDBFactory* leveldb_factory) {
+ LevelDBFactory* leveldb_factory,
+ base::TaskRunner* task_runner) {
IDB_TRACE("IndexedDBBackingStore::Open");
DCHECK(!path_base.empty());
*data_loss = blink::WebIDBDataLossNone;
@@ -749,20 +760,27 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
return scoped_refptr<IndexedDBBackingStore>();
}
- return Create(origin_url, db.Pass(), comparator.Pass());
+ return Create(indexed_db_factory,
+ origin_url,
+ db.Pass(),
+ comparator.Pass(),
+ task_runner);
}
// static
scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
- const GURL& origin_url) {
+ const GURL& origin_url,
+ base::TaskRunner* task_runner) {
DefaultLevelDBFactory leveldb_factory;
- return IndexedDBBackingStore::OpenInMemory(origin_url, &leveldb_factory);
+ return IndexedDBBackingStore::OpenInMemory(
+ origin_url, &leveldb_factory, task_runner);
}
// static
scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
const GURL& origin_url,
- LevelDBFactory* leveldb_factory) {
+ LevelDBFactory* leveldb_factory,
+ base::TaskRunner* task_runner) {
IDB_TRACE("IndexedDBBackingStore::OpenInMemory");
scoped_ptr<LevelDBComparator> comparator(new Comparator());
@@ -776,18 +794,28 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
}
HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS, origin_url);
- return Create(origin_url, db.Pass(), comparator.Pass());
+ return Create(NULL /* indexed_db_factory */,
+ origin_url,
+ db.Pass(),
+ comparator.Pass(),
+ task_runner);
}
// static
scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create(
+ IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
scoped_ptr<LevelDBDatabase> db,
- scoped_ptr<LevelDBComparator> comparator) {
+ scoped_ptr<LevelDBComparator> comparator,
+ base::TaskRunner* task_runner) {
// TODO(jsbell): Handle comparator name changes.
scoped_refptr<IndexedDBBackingStore> backing_store(
- new IndexedDBBackingStore(origin_url, db.Pass(), comparator.Pass()));
+ new IndexedDBBackingStore(indexed_db_factory,
+ origin_url,
+ db.Pass(),
+ comparator.Pass(),
+ task_runner));
if (!SetUpMetadata(backing_store->db_.get(),
backing_store->origin_identifier_))
return scoped_refptr<IndexedDBBackingStore>();
@@ -1979,6 +2007,11 @@ leveldb::Status IndexedDBBackingStore::KeyExistsInIndex(
return InvalidDBKeyStatus();
}
+void IndexedDBBackingStore::ReportBlobUnused(int64 database_id,
+ int64 blob_key) {
+ // TODO(ericu)
+}
+
IndexedDBBackingStore::Cursor::Cursor(
const IndexedDBBackingStore::Cursor* other)
: transaction_(other->transaction_),
diff --git a/content/browser/indexed_db/indexed_db_backing_store.h b/content/browser/indexed_db/indexed_db_backing_store.h
index 83ce99f..ed2e395 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.h
+++ b/content/browser/indexed_db/indexed_db_backing_store.h
@@ -15,6 +15,7 @@
#include "base/strings/string_piece.h"
#include "base/timer/timer.h"
#include "content/browser/indexed_db/indexed_db.h"
+#include "content/browser/indexed_db/indexed_db_active_blob_registry.h"
#include "content/browser/indexed_db/indexed_db_metadata.h"
#include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
#include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
@@ -25,8 +26,13 @@
#include "third_party/leveldatabase/src/include/leveldb/status.h"
#include "url/gurl.h"
+namespace base {
+class TaskRunner;
+}
+
namespace content {
+class IndexedDBFactory;
class LevelDBComparator;
class LevelDBDatabase;
struct IndexedDBValue;
@@ -54,29 +60,40 @@ class CONTENT_EXPORT IndexedDBBackingStore
};
const GURL& origin_url() const { return origin_url_; }
+ IndexedDBFactory* factory() const { return indexed_db_factory_; }
+ base::TaskRunner* task_runner() const { return task_runner_; }
base::OneShotTimer<IndexedDBBackingStore>* close_timer() {
return &close_timer_;
}
+ IndexedDBActiveBlobRegistry* active_blob_registry() {
+ return &active_blob_registry_;
+ }
static scoped_refptr<IndexedDBBackingStore> Open(
+ IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
const base::FilePath& path_base,
blink::WebIDBDataLoss* data_loss,
std::string* data_loss_message,
- bool* disk_full);
+ bool* disk_full,
+ base::TaskRunner* task_runner);
static scoped_refptr<IndexedDBBackingStore> Open(
+ IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
const base::FilePath& path_base,
blink::WebIDBDataLoss* data_loss,
std::string* data_loss_message,
bool* disk_full,
- LevelDBFactory* factory);
+ LevelDBFactory* leveldb_factory,
+ base::TaskRunner* task_runner);
static scoped_refptr<IndexedDBBackingStore> OpenInMemory(
- const GURL& origin_url);
+ const GURL& origin_url,
+ base::TaskRunner* task_runner);
static scoped_refptr<IndexedDBBackingStore> OpenInMemory(
const GURL& origin_url,
- LevelDBFactory* factory);
+ LevelDBFactory* level_db_factory,
+ base::TaskRunner* task_runner);
// Compact is public for testing.
virtual void Compact();
@@ -216,6 +233,9 @@ class CONTENT_EXPORT IndexedDBBackingStore
scoped_ptr<IndexedDBKey>* found_primary_key,
bool* exists) WARN_UNUSED_RESULT;
+ // Public for IndexedDBActiveBlobRegistry::ReleaseBlobRef.
+ virtual void ReportBlobUnused(int64 database_id, int64 blob_key);
+
class Cursor {
public:
virtual ~Cursor();
@@ -322,17 +342,22 @@ class CONTENT_EXPORT IndexedDBBackingStore
};
protected:
- IndexedDBBackingStore(const GURL& origin_url,
+ IndexedDBBackingStore(IndexedDBFactory* indexed_db_factory,
+ const GURL& origin_url,
scoped_ptr<LevelDBDatabase> db,
- scoped_ptr<LevelDBComparator> comparator);
+ scoped_ptr<LevelDBComparator> comparator,
+ base::TaskRunner* task_runner);
virtual ~IndexedDBBackingStore();
friend class base::RefCounted<IndexedDBBackingStore>;
private:
static scoped_refptr<IndexedDBBackingStore> Create(
+ IndexedDBFactory* indexed_db_factory,
const GURL& origin_url,
scoped_ptr<LevelDBDatabase> db,
- scoped_ptr<LevelDBComparator> comparator);
+ scoped_ptr<LevelDBComparator> comparator,
+ base::TaskRunner* task_runner);
+
static bool ReadCorruptionInfo(const base::FilePath& path_base,
const GURL& origin_url,
std::string& message);
@@ -350,6 +375,7 @@ class CONTENT_EXPORT IndexedDBBackingStore
IndexedDBObjectStoreMetadata::IndexMap* map)
WARN_UNUSED_RESULT;
+ IndexedDBFactory* indexed_db_factory_;
const GURL origin_url_;
// The origin identifier is a key prefix unique to the origin used in the
@@ -359,9 +385,13 @@ class CONTENT_EXPORT IndexedDBBackingStore
// this is redundant but necessary for backwards compatibility; the suffix
// provides for future flexibility.
const std::string origin_identifier_;
+ base::TaskRunner* task_runner_;
scoped_ptr<LevelDBDatabase> db_;
scoped_ptr<LevelDBComparator> comparator_;
+ // Whenever blobs are registered in active_blob_registry_, indexed_db_factory_
+ // will hold a reference to this backing store.
+ IndexedDBActiveBlobRegistry active_blob_registry_;
base::OneShotTimer<IndexedDBBackingStore> close_timer_;
};
diff --git a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
index 63a7fa4..6885faa 100644
--- a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
@@ -23,7 +23,8 @@ class IndexedDBBackingStoreTest : public testing::Test {
IndexedDBBackingStoreTest() {}
virtual void SetUp() {
const GURL origin("http://localhost:81");
- backing_store_ = IndexedDBBackingStore::OpenInMemory(origin);
+ backing_store_ =
+ IndexedDBBackingStore::OpenInMemory(origin, NULL /* task_runner */);
// useful keys and values during tests
m_value1 = IndexedDBValue("value1", std::vector<IndexedDBBlobInfo>());
diff --git a/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc b/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc
index b097207..a3e2d03 100644
--- a/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc
@@ -21,6 +21,14 @@ using content::LevelDBDatabase;
using content::LevelDBFactory;
using content::LevelDBSnapshot;
+namespace base {
+class TaskRunner;
+}
+
+namespace content {
+class IndexedDBFactory;
+}
+
namespace {
class BustedLevelDBDatabase : public LevelDBDatabase {
@@ -62,6 +70,7 @@ class MockLevelDBFactory : public LevelDBFactory {
};
TEST(IndexedDBIOErrorTest, CleanUpTest) {
+ content::IndexedDBFactory* factory = NULL;
const GURL origin("http://localhost:81");
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
@@ -71,13 +80,16 @@ TEST(IndexedDBIOErrorTest, CleanUpTest) {
blink::WebIDBDataLossNone;
std::string data_loss_message;
bool disk_full = false;
+ base::TaskRunner* task_runner = NULL;
scoped_refptr<IndexedDBBackingStore> backing_store =
- IndexedDBBackingStore::Open(origin,
+ IndexedDBBackingStore::Open(factory,
+ origin,
path,
&data_loss,
&data_loss_message,
&disk_full,
- &mock_leveldb_factory);
+ &mock_leveldb_factory,
+ task_runner);
}
// TODO(dgrogan): Remove expect_destroy if we end up not using it again. It is
@@ -114,6 +126,7 @@ class MockErrorLevelDBFactory : public LevelDBFactory {
};
TEST(IndexedDBNonRecoverableIOErrorTest, NuancedCleanupTest) {
+ content::IndexedDBFactory* factory = NULL;
const GURL origin("http://localhost:81");
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
@@ -122,44 +135,53 @@ TEST(IndexedDBNonRecoverableIOErrorTest, NuancedCleanupTest) {
blink::WebIDBDataLossNone;
std::string data_loss_reason;
bool disk_full = false;
+ base::TaskRunner* task_runner = NULL;
MockErrorLevelDBFactory<int> mock_leveldb_factory(ENOSPC, false);
scoped_refptr<IndexedDBBackingStore> backing_store =
- IndexedDBBackingStore::Open(origin,
+ IndexedDBBackingStore::Open(factory,
+ origin,
path,
&data_loss,
&data_loss_reason,
&disk_full,
- &mock_leveldb_factory);
+ &mock_leveldb_factory,
+ task_runner);
MockErrorLevelDBFactory<base::File::Error> mock_leveldb_factory2(
base::File::FILE_ERROR_NO_MEMORY, false);
scoped_refptr<IndexedDBBackingStore> backing_store2 =
- IndexedDBBackingStore::Open(origin,
+ IndexedDBBackingStore::Open(factory,
+ origin,
path,
&data_loss,
&data_loss_reason,
&disk_full,
- &mock_leveldb_factory2);
+ &mock_leveldb_factory2,
+ task_runner);
MockErrorLevelDBFactory<int> mock_leveldb_factory3(EIO, false);
scoped_refptr<IndexedDBBackingStore> backing_store3 =
- IndexedDBBackingStore::Open(origin,
+ IndexedDBBackingStore::Open(factory,
+ origin,
path,
&data_loss,
&data_loss_reason,
&disk_full,
- &mock_leveldb_factory3);
+ &mock_leveldb_factory3,
+ task_runner);
MockErrorLevelDBFactory<base::File::Error> mock_leveldb_factory4(
base::File::FILE_ERROR_FAILED, false);
scoped_refptr<IndexedDBBackingStore> backing_store4 =
- IndexedDBBackingStore::Open(origin,
+ IndexedDBBackingStore::Open(factory,
+ origin,
path,
&data_loss,
&data_loss_reason,
&disk_full,
- &mock_leveldb_factory4);
+ &mock_leveldb_factory4,
+ task_runner);
}
} // namespace
diff --git a/content/browser/indexed_db/indexed_db_factory.cc b/content/browser/indexed_db/indexed_db_factory.cc
index 1130603..e3a415a 100644
--- a/content/browser/indexed_db/indexed_db_factory.cc
+++ b/content/browser/indexed_db/indexed_db_factory.cc
@@ -60,6 +60,15 @@ void IndexedDBFactory::ReleaseDatabase(
void IndexedDBFactory::ReleaseBackingStore(const GURL& origin_url,
bool immediate) {
+ if (immediate) {
+ IndexedDBBackingStoreMap::iterator it =
+ backing_stores_with_active_blobs_.find(origin_url);
+ if (it != backing_stores_with_active_blobs_.end()) {
+ it->second->active_blob_registry()->ForceShutdown();
+ backing_stores_with_active_blobs_.erase(it);
+ }
+ }
+
// Only close if this is the last reference.
if (!HasLastBackingStoreReference(origin_url))
return;
@@ -134,9 +143,31 @@ void IndexedDBFactory::ContextDestroyed() {
++it)
it->second->close_timer()->Stop();
backing_store_map_.clear();
+ backing_stores_with_active_blobs_.clear();
context_ = NULL;
}
+void IndexedDBFactory::ReportOutstandingBlobs(const GURL& origin_url,
+ bool blobs_outstanding) {
+ if (!context_)
+ return;
+ if (blobs_outstanding) {
+ DCHECK(!backing_stores_with_active_blobs_.count(origin_url));
+ IndexedDBBackingStoreMap::iterator it = backing_store_map_.find(origin_url);
+ if (it != backing_store_map_.end())
+ backing_stores_with_active_blobs_.insert(*it);
+ else
+ DCHECK(false);
+ } else {
+ IndexedDBBackingStoreMap::iterator it =
+ backing_stores_with_active_blobs_.find(origin_url);
+ if (it != backing_stores_with_active_blobs_.end()) {
+ backing_stores_with_active_blobs_.erase(it);
+ ReleaseBackingStore(origin_url, false /* immediate */);
+ }
+ }
+}
+
void IndexedDBFactory::GetDatabaseNames(
scoped_refptr<IndexedDBCallbacks> callbacks,
const GURL& origin_url,
@@ -288,13 +319,16 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStore(
scoped_refptr<IndexedDBBackingStore> backing_store;
if (open_in_memory) {
- backing_store = IndexedDBBackingStore::OpenInMemory(origin_url);
+ backing_store =
+ IndexedDBBackingStore::OpenInMemory(origin_url, context_->TaskRunner());
} else {
- backing_store = IndexedDBBackingStore::Open(origin_url,
+ backing_store = IndexedDBBackingStore::Open(this,
+ origin_url,
data_directory,
data_loss,
data_loss_message,
- disk_full);
+ disk_full,
+ context_->TaskRunner());
}
if (backing_store.get()) {
diff --git a/content/browser/indexed_db/indexed_db_factory.h b/content/browser/indexed_db/indexed_db_factory.h
index de2e1dd..3b5a947 100644
--- a/content/browser/indexed_db/indexed_db_factory.h
+++ b/content/browser/indexed_db/indexed_db_factory.h
@@ -61,6 +61,10 @@ class CONTENT_EXPORT IndexedDBFactory
// Called by the IndexedDBContext destructor so the factory can do cleanup.
void ContextDestroyed();
+ // Called by the IndexedDBActiveBlobRegistry.
+ virtual void ReportOutstandingBlobs(const GURL& origin_url,
+ bool blobs_outstanding);
+
// Called by an IndexedDBDatabase when it is actually deleted.
void DatabaseDeleted(const IndexedDBDatabase::Identifier& identifier);
@@ -120,6 +124,7 @@ class CONTENT_EXPORT IndexedDBFactory
IndexedDBBackingStoreMap backing_store_map_;
std::set<scoped_refptr<IndexedDBBackingStore> > session_only_backing_stores_;
+ IndexedDBBackingStoreMap backing_stores_with_active_blobs_;
};
} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_factory_unittest.cc b/content/browser/indexed_db/indexed_db_factory_unittest.cc
index 1038035..1290856 100644
--- a/content/browser/indexed_db/indexed_db_factory_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_factory_unittest.cc
@@ -9,7 +9,9 @@
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/test_simple_task_runner.h"
#include "content/browser/indexed_db/indexed_db_connection.h"
+#include "content/browser/indexed_db/indexed_db_context_impl.h"
#include "content/browser/indexed_db/mock_indexed_db_callbacks.h"
#include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -22,21 +24,11 @@ using base::ASCIIToUTF16;
namespace content {
-class IndexedDBFactoryTest : public testing::Test {
- public:
- IndexedDBFactoryTest() {}
-
- protected:
- // For timers to post events.
- base::MessageLoop loop_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(IndexedDBFactoryTest);
-};
+namespace {
class MockIDBFactory : public IndexedDBFactory {
public:
- MockIDBFactory() : IndexedDBFactory(NULL) {}
+ MockIDBFactory(IndexedDBContextImpl* context) : IndexedDBFactory(context) {}
scoped_refptr<IndexedDBBackingStore> TestOpenBackingStore(
const GURL& origin,
const base::FilePath& data_directory) {
@@ -67,26 +59,53 @@ class MockIDBFactory : public IndexedDBFactory {
virtual ~MockIDBFactory() {}
};
+} // namespace
+
+class IndexedDBFactoryTest : public testing::Test {
+ public:
+ IndexedDBFactoryTest() {
+ task_runner_ = new base::TestSimpleTaskRunner();
+ context_ = new IndexedDBContextImpl(base::FilePath(),
+ NULL /* special_storage_policy */,
+ NULL /* quota_manager_proxy */,
+ task_runner_.get());
+ idb_factory_ = new MockIDBFactory(context_.get());
+ }
+
+ protected:
+ // For timers to post events.
+ base::MessageLoop loop_;
+
+ MockIDBFactory* factory() const { return idb_factory_.get(); }
+ void clear_factory() { idb_factory_ = NULL; }
+ IndexedDBContextImpl* context() const { return context_.get(); }
+
+ private:
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ scoped_refptr<IndexedDBContextImpl> context_;
+ scoped_refptr<MockIDBFactory> idb_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(IndexedDBFactoryTest);
+};
+
TEST_F(IndexedDBFactoryTest, BackingStoreLifetime) {
GURL origin1("http://localhost:81");
GURL origin2("http://localhost:82");
- scoped_refptr<MockIDBFactory> factory = new MockIDBFactory();
-
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
scoped_refptr<IndexedDBBackingStore> disk_store1 =
- factory->TestOpenBackingStore(origin1, temp_directory.path());
+ factory()->TestOpenBackingStore(origin1, temp_directory.path());
scoped_refptr<IndexedDBBackingStore> disk_store2 =
- factory->TestOpenBackingStore(origin1, temp_directory.path());
+ factory()->TestOpenBackingStore(origin1, temp_directory.path());
EXPECT_EQ(disk_store1.get(), disk_store2.get());
scoped_refptr<IndexedDBBackingStore> disk_store3 =
- factory->TestOpenBackingStore(origin2, temp_directory.path());
+ factory()->TestOpenBackingStore(origin2, temp_directory.path());
- factory->TestCloseBackingStore(disk_store1);
- factory->TestCloseBackingStore(disk_store3);
+ factory()->TestCloseBackingStore(disk_store1);
+ factory()->TestCloseBackingStore(disk_store3);
EXPECT_FALSE(disk_store1->HasOneRef());
EXPECT_FALSE(disk_store2->HasOneRef());
@@ -99,30 +118,28 @@ TEST_F(IndexedDBFactoryTest, BackingStoreLifetime) {
TEST_F(IndexedDBFactoryTest, BackingStoreLazyClose) {
GURL origin("http://localhost:81");
- scoped_refptr<MockIDBFactory> factory = new MockIDBFactory();
-
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
scoped_refptr<IndexedDBBackingStore> store =
- factory->TestOpenBackingStore(origin, temp_directory.path());
+ factory()->TestOpenBackingStore(origin, temp_directory.path());
// Give up the local refptr so that the factory has the only
// outstanding reference.
IndexedDBBackingStore* store_ptr = store.get();
store = NULL;
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
- factory->TestReleaseBackingStore(store_ptr, false);
+ factory()->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
- factory->TestOpenBackingStore(origin, temp_directory.path());
+ factory()->TestOpenBackingStore(origin, temp_directory.path());
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
- factory->TestReleaseBackingStore(store_ptr, false);
+ factory()->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
// Take back a ref ptr and ensure that the actual close
// stops a running timer.
store = store_ptr;
- factory->TestCloseBackingStore(store_ptr);
+ factory()->TestCloseBackingStore(store_ptr);
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
}
@@ -130,25 +147,24 @@ TEST_F(IndexedDBFactoryTest, MemoryBackingStoreLifetime) {
GURL origin1("http://localhost:81");
GURL origin2("http://localhost:82");
- scoped_refptr<MockIDBFactory> factory = new MockIDBFactory();
scoped_refptr<IndexedDBBackingStore> mem_store1 =
- factory->TestOpenBackingStore(origin1, base::FilePath());
+ factory()->TestOpenBackingStore(origin1, base::FilePath());
scoped_refptr<IndexedDBBackingStore> mem_store2 =
- factory->TestOpenBackingStore(origin1, base::FilePath());
+ factory()->TestOpenBackingStore(origin1, base::FilePath());
EXPECT_EQ(mem_store1.get(), mem_store2.get());
scoped_refptr<IndexedDBBackingStore> mem_store3 =
- factory->TestOpenBackingStore(origin2, base::FilePath());
+ factory()->TestOpenBackingStore(origin2, base::FilePath());
- factory->TestCloseBackingStore(mem_store1);
- factory->TestCloseBackingStore(mem_store3);
+ factory()->TestCloseBackingStore(mem_store1);
+ factory()->TestCloseBackingStore(mem_store3);
EXPECT_FALSE(mem_store1->HasOneRef());
EXPECT_FALSE(mem_store2->HasOneRef());
EXPECT_FALSE(mem_store3->HasOneRef());
- factory = NULL;
+ clear_factory();
EXPECT_FALSE(mem_store1->HasOneRef()); // mem_store1 and 2
EXPECT_FALSE(mem_store2->HasOneRef()); // mem_store1 and 2
EXPECT_TRUE(mem_store3->HasOneRef());
@@ -161,7 +177,6 @@ TEST_F(IndexedDBFactoryTest, RejectLongOrigins) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
const base::FilePath base_path = temp_directory.path();
- scoped_refptr<MockIDBFactory> factory = new MockIDBFactory();
int limit = base::GetMaximumPathComponentLength(base_path);
EXPECT_GT(limit, 0);
@@ -169,18 +184,18 @@ TEST_F(IndexedDBFactoryTest, RejectLongOrigins) {
std::string origin(limit + 1, 'x');
GURL too_long_origin("http://" + origin + ":81/");
scoped_refptr<IndexedDBBackingStore> diskStore1 =
- factory->TestOpenBackingStore(too_long_origin, base_path);
+ factory()->TestOpenBackingStore(too_long_origin, base_path);
EXPECT_FALSE(diskStore1);
GURL ok_origin("http://someorigin.com:82/");
scoped_refptr<IndexedDBBackingStore> diskStore2 =
- factory->TestOpenBackingStore(ok_origin, base_path);
+ factory()->TestOpenBackingStore(ok_origin, base_path);
EXPECT_TRUE(diskStore2);
}
class DiskFullFactory : public IndexedDBFactory {
public:
- DiskFullFactory() : IndexedDBFactory(NULL) {}
+ DiskFullFactory(IndexedDBContextImpl* context) : IndexedDBFactory(context) {}
private:
virtual ~DiskFullFactory() {}
@@ -211,8 +226,10 @@ class LookingForQuotaErrorMockCallbacks : public IndexedDBCallbacks {
TEST_F(IndexedDBFactoryTest, QuotaErrorOnDiskFull) {
const GURL origin("http://localhost:81");
+ base::ScopedTempDir temp_directory;
+ ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- scoped_refptr<DiskFullFactory> factory = new DiskFullFactory;
+ scoped_refptr<DiskFullFactory> factory = new DiskFullFactory(context());
scoped_refptr<LookingForQuotaErrorMockCallbacks> callbacks =
new LookingForQuotaErrorMockCallbacks;
scoped_refptr<IndexedDBDatabaseCallbacks> dummy_database_callbacks =
@@ -223,8 +240,7 @@ TEST_F(IndexedDBFactoryTest, QuotaErrorOnDiskFull) {
0, /* child_process_id */
2, /* transaction_id */
1 /* version */);
- factory->Open(
- name, connection, origin, base::FilePath(FILE_PATH_LITERAL("/dummy")));
+ factory->Open(name, connection, origin, temp_directory.path());
}
TEST_F(IndexedDBFactoryTest, BackingStoreReleasedOnForcedClose) {
@@ -233,8 +249,6 @@ TEST_F(IndexedDBFactoryTest, BackingStoreReleasedOnForcedClose) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- scoped_refptr<IndexedDBFactory> factory = new IndexedDBFactory(NULL);
-
scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks());
scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
new MockIndexedDBDatabaseCallbacks());
@@ -245,17 +259,18 @@ TEST_F(IndexedDBFactoryTest, BackingStoreReleasedOnForcedClose) {
0, /* child_process_id */
transaction_id,
IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
- factory->Open(ASCIIToUTF16("db"), connection, origin, temp_directory.path());
+ factory()->Open(
+ ASCIIToUTF16("db"), connection, origin, temp_directory.path());
EXPECT_TRUE(callbacks->connection());
- EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
callbacks->connection()->ForceClose();
- EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
}
TEST_F(IndexedDBFactoryTest, BackingStoreReleaseDelayedOnClose) {
@@ -264,8 +279,6 @@ TEST_F(IndexedDBFactoryTest, BackingStoreReleaseDelayedOnClose) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- scoped_refptr<IndexedDBFactory> factory = new IndexedDBFactory(NULL);
-
scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks());
scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
new MockIndexedDBDatabaseCallbacks());
@@ -276,28 +289,29 @@ TEST_F(IndexedDBFactoryTest, BackingStoreReleaseDelayedOnClose) {
0, /* child_process_id */
transaction_id,
IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
- factory->Open(ASCIIToUTF16("db"), connection, origin, temp_directory.path());
+ factory()->Open(
+ ASCIIToUTF16("db"), connection, origin, temp_directory.path());
EXPECT_TRUE(callbacks->connection());
IndexedDBBackingStore* store =
callbacks->connection()->database()->backing_store();
EXPECT_FALSE(store->HasOneRef()); // Factory and database.
- EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
+ EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
callbacks->connection()->Close();
EXPECT_TRUE(store->HasOneRef()); // Factory.
- EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
- EXPECT_TRUE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
EXPECT_TRUE(store->close_timer()->IsRunning());
// Take a ref so it won't be destroyed out from under the test.
scoped_refptr<IndexedDBBackingStore> store_ref = store;
// Now simulate shutdown, which should stop the timer.
- factory->ContextDestroyed();
+ factory()->ContextDestroyed();
EXPECT_TRUE(store->HasOneRef()); // Local.
EXPECT_FALSE(store->close_timer()->IsRunning());
- EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
}
TEST_F(IndexedDBFactoryTest, DeleteDatabaseClosesBackingStore) {
@@ -306,25 +320,22 @@ TEST_F(IndexedDBFactoryTest, DeleteDatabaseClosesBackingStore) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- scoped_refptr<IndexedDBFactory> factory = new IndexedDBFactory(NULL);
- EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
const bool expect_connection = false;
scoped_refptr<MockIndexedDBCallbacks> callbacks(
new MockIndexedDBCallbacks(expect_connection));
- factory->DeleteDatabase(ASCIIToUTF16("db"),
- callbacks,
- origin,
- temp_directory.path());
+ factory()->DeleteDatabase(
+ ASCIIToUTF16("db"), callbacks, origin, temp_directory.path());
- EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
- EXPECT_TRUE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
// Now simulate shutdown, which should stop the timer.
- factory->ContextDestroyed();
+ factory()->ContextDestroyed();
- EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
}
TEST_F(IndexedDBFactoryTest, GetDatabaseNamesClosesBackingStore) {
@@ -333,24 +344,21 @@ TEST_F(IndexedDBFactoryTest, GetDatabaseNamesClosesBackingStore) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- scoped_refptr<IndexedDBFactory> factory = new IndexedDBFactory(NULL);
- EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
const bool expect_connection = false;
scoped_refptr<MockIndexedDBCallbacks> callbacks(
new MockIndexedDBCallbacks(expect_connection));
- factory->GetDatabaseNames(callbacks,
- origin,
- temp_directory.path());
+ factory()->GetDatabaseNames(callbacks, origin, temp_directory.path());
- EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
- EXPECT_TRUE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
// Now simulate shutdown, which should stop the timer.
- factory->ContextDestroyed();
+ factory()->ContextDestroyed();
- EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
}
TEST_F(IndexedDBFactoryTest, ForceCloseReleasesBackingStore) {
@@ -359,8 +367,6 @@ TEST_F(IndexedDBFactoryTest, ForceCloseReleasesBackingStore) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- scoped_refptr<IndexedDBFactory> factory = new IndexedDBFactory(NULL);
-
scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks());
scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
new MockIndexedDBDatabaseCallbacks());
@@ -371,24 +377,25 @@ TEST_F(IndexedDBFactoryTest, ForceCloseReleasesBackingStore) {
0, /* child_process_id */
transaction_id,
IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
- factory->Open(ASCIIToUTF16("db"), connection, origin, temp_directory.path());
+ factory()->Open(
+ ASCIIToUTF16("db"), connection, origin, temp_directory.path());
EXPECT_TRUE(callbacks->connection());
- EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
callbacks->connection()->Close();
- EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
- EXPECT_TRUE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_TRUE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_TRUE(factory()->IsBackingStorePendingClose(origin));
- factory->ForceClose(origin);
+ factory()->ForceClose(origin);
- EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
- EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
+ EXPECT_FALSE(factory()->IsBackingStoreOpen(origin));
+ EXPECT_FALSE(factory()->IsBackingStorePendingClose(origin));
// Ensure it is safe if the store is not open.
- factory->ForceClose(origin);
+ factory()->ForceClose(origin);
}
class UpgradeNeededCallbacks : public MockIndexedDBCallbacks {
@@ -431,7 +438,6 @@ TEST_F(IndexedDBFactoryTest, DatabaseFailedOpen) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- scoped_refptr<IndexedDBFactory> factory = new IndexedDBFactory(NULL);
const base::string16 db_name(ASCIIToUTF16("db"));
const int64 db_version = 2;
const int64 transaction_id = 1;
@@ -447,8 +453,8 @@ TEST_F(IndexedDBFactoryTest, DatabaseFailedOpen) {
0, /* child_process_id */
transaction_id,
db_version);
- factory->Open(db_name, connection, origin, temp_directory.path());
- EXPECT_TRUE(factory->IsDatabaseOpen(origin, db_name));
+ factory()->Open(db_name, connection, origin, temp_directory.path());
+ EXPECT_TRUE(factory()->IsDatabaseOpen(origin, db_name));
// Pump the message loop so the upgrade transaction can run.
base::MessageLoop::current()->RunUntilIdle();
@@ -456,7 +462,7 @@ TEST_F(IndexedDBFactoryTest, DatabaseFailedOpen) {
callbacks->connection()->database()->Commit(transaction_id);
callbacks->connection()->Close();
- EXPECT_FALSE(factory->IsDatabaseOpen(origin, db_name));
+ EXPECT_FALSE(factory()->IsDatabaseOpen(origin, db_name));
}
// Open at version < 2, which will fail; ensure factory doesn't retain
@@ -468,12 +474,12 @@ TEST_F(IndexedDBFactoryTest, DatabaseFailedOpen) {
0, /* child_process_id */
transaction_id,
db_version - 1);
- factory->Open(db_name, connection, origin, temp_directory.path());
- EXPECT_FALSE(factory->IsDatabaseOpen(origin, db_name));
+ factory()->Open(db_name, connection, origin, temp_directory.path());
+ EXPECT_FALSE(factory()->IsDatabaseOpen(origin, db_name));
}
// Terminate all pending-close timers.
- factory->ForceClose(origin);
+ factory()->ForceClose(origin);
}
} // namespace content
diff --git a/content/browser/indexed_db/indexed_db_fake_backing_store.cc b/content/browser/indexed_db/indexed_db_fake_backing_store.cc
index 18ca50c..a40e576 100644
--- a/content/browser/indexed_db/indexed_db_fake_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_fake_backing_store.cc
@@ -8,6 +8,22 @@
namespace content {
+IndexedDBFakeBackingStore::IndexedDBFakeBackingStore()
+ : IndexedDBBackingStore(NULL,
+ GURL("http://localhost:81"),
+ scoped_ptr<LevelDBDatabase>(),
+ scoped_ptr<LevelDBComparator>(),
+ NULL) {}
+
+IndexedDBFakeBackingStore::IndexedDBFakeBackingStore(
+ IndexedDBFactory* factory,
+ base::TaskRunner* task_runner)
+ : IndexedDBBackingStore(factory,
+ GURL("http://localhost:81"),
+ scoped_ptr<LevelDBDatabase>(),
+ scoped_ptr<LevelDBComparator>(),
+ task_runner) {}
+
IndexedDBFakeBackingStore::~IndexedDBFakeBackingStore() {}
std::vector<base::string16> IndexedDBFakeBackingStore::GetDatabaseNames() {
@@ -113,6 +129,9 @@ leveldb::Status IndexedDBFakeBackingStore::PutIndexDataForRecord(
return leveldb::Status::IOError("test error");
}
+void IndexedDBFakeBackingStore::ReportBlobUnused(int64 database_id,
+ int64 blob_key) {}
+
scoped_ptr<IndexedDBBackingStore::Cursor>
IndexedDBFakeBackingStore::OpenObjectStoreKeyCursor(
IndexedDBBackingStore::Transaction* transaction,
diff --git a/content/browser/indexed_db/indexed_db_fake_backing_store.h b/content/browser/indexed_db/indexed_db_fake_backing_store.h
index 113a056..f4f67d7 100644
--- a/content/browser/indexed_db/indexed_db_fake_backing_store.h
+++ b/content/browser/indexed_db/indexed_db_fake_backing_store.h
@@ -9,14 +9,19 @@
#include "content/browser/indexed_db/indexed_db_backing_store.h"
+namespace base {
+class TaskRunner;
+}
+
namespace content {
+class IndexedDBFactory;
+
class IndexedDBFakeBackingStore : public IndexedDBBackingStore {
public:
- IndexedDBFakeBackingStore()
- : IndexedDBBackingStore(GURL("http://localhost:81"),
- scoped_ptr<LevelDBDatabase>(),
- scoped_ptr<LevelDBComparator>()) {}
+ IndexedDBFakeBackingStore();
+ IndexedDBFakeBackingStore(IndexedDBFactory* factory,
+ base::TaskRunner* task_runner);
virtual std::vector<base::string16> GetDatabaseNames() OVERRIDE;
virtual leveldb::Status GetIDBDatabaseMetaData(const base::string16& name,
IndexedDBDatabaseMetadata*,
@@ -83,6 +88,7 @@ class IndexedDBFakeBackingStore : public IndexedDBBackingStore {
const IndexedDBKey&,
const RecordIdentifier&)
OVERRIDE;
+ virtual void ReportBlobUnused(int64 database_id, int64 blob_key) OVERRIDE;
virtual scoped_ptr<Cursor> OpenObjectStoreKeyCursor(
Transaction* transaction,
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index efeaf69..34fca9c 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -662,6 +662,8 @@
'browser/host_zoom_map_impl.cc',
'browser/host_zoom_map_impl.h',
'browser/indexed_db/indexed_db.h',
+ 'browser/indexed_db/indexed_db_active_blob_registry.cc',
+ 'browser/indexed_db/indexed_db_active_blob_registry.h',
'browser/indexed_db/indexed_db_backing_store.cc',
'browser/indexed_db/indexed_db_backing_store.h',
'browser/indexed_db/indexed_db_blob_info.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 66fb4c0..488e552 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -399,6 +399,7 @@
'browser/geolocation/wifi_data_provider_unittest_win.cc',
'browser/gpu/shader_disk_cache_unittest.cc',
'browser/host_zoom_map_impl_unittest.cc',
+ 'browser/indexed_db/indexed_db_active_blob_registry_unittest.cc',
'browser/indexed_db/indexed_db_backing_store_unittest.cc',
'browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc',
'browser/indexed_db/indexed_db_database_unittest.cc',