diff options
author | ericu@chromium.org <ericu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-03 16:26:19 +0000 |
---|---|---|
committer | ericu@chromium.org <ericu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-03 16:26:19 +0000 |
commit | 3cf0cae325bf82ed3b5523b4696d8899b57e8774 (patch) | |
tree | a3db6339b6727ee3cdfac3b35169e3ef48875789 | |
parent | 174e3bab30cc18b9b7956b5c423f4f0610c68bde (diff) | |
download | chromium_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
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', |