#include <inttypes.h>
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <ostream>
#include <set>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include "base/auto_reset.h"
#include "base/barrier_closure.h"
#include "base/check.h"
#include "base/containers/contains.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/run_until.h"
#include "base/test/task_environment.h"
#include "base/test/test_file_util.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "components/services/storage/indexed_db/locks/partitioned_lock_id.h"
#include "components/services/storage/privileged/cpp/bucket_client_info.h"
#include "components/services/storage/privileged/mojom/indexed_db_client_state_checker.mojom.h"
#include "components/services/storage/privileged/mojom/indexed_db_control.mojom.h"
#include "components/services/storage/public/cpp/buckets/bucket_id.h"
#include "components/services/storage/public/cpp/buckets/bucket_info.h"
#include "components/services/storage/public/cpp/buckets/bucket_init_params.h"
#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
#include "components/services/storage/public/cpp/buckets/constants.h"
#include "components/services/storage/public/cpp/quota_error_or.h"
#include "components/services/storage/public/mojom/storage_policy_update.mojom.h"
#include "content/browser/indexed_db/indexed_db_context_impl.h"
#include "content/browser/indexed_db/indexed_db_data_format_version.h"
#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
#include "content/browser/indexed_db/instance/bucket_context.h"
#include "content/browser/indexed_db/instance/bucket_context_handle.h"
#include "content/browser/indexed_db/instance/connection.h"
#include "content/browser/indexed_db/instance/leveldb/backing_store.h"
#include "content/browser/indexed_db/instance/mock_blob_storage_context.h"
#include "content/browser/indexed_db/mock_mojo_indexed_db_database_callbacks.h"
#include "content/browser/indexed_db/mock_mojo_indexed_db_factory_client.h"
#include "content/browser/indexed_db/status.h"
#include "env_chromium.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/struct_ptr.h"
#include "mojo/public/cpp/test_support/test_utils.h"
#include "net/base/features.h"
#include "net/base/schemeful_site.h"
#include "storage/browser/test/mock_quota_manager.h"
#include "storage/browser/test/mock_quota_manager_proxy.h"
#include "storage/browser/test/mock_special_storage_policy.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
#include "third_party/blink/public/common/indexeddb/indexeddb_key_path.h"
#include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
#include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h"
#include "third_party/blink/public/mojom/storage_key/ancestor_chain_bit.mojom-shared.h"
#include "url/gurl.h"
#include "url/origin.h"
using base::test::RunClosure;
using blink::IndexedDBDatabaseMetadata;
using blink::IndexedDBIndexKeys;
using blink::IndexedDBKey;
using storage::BucketLocator;
using testing::_;
using testing::StrictMock;
using url::Origin;
namespace content::indexed_db {
namespace {
constexpr char16_t kDatabaseName[] = u"db";
constexpr char kOrigin[] = "https://www.example.com";
url::Origin ToOrigin(const std::string& url) {
return url::Origin::Create(GURL(url));
}
MATCHER_P(IsAssociatedInterfacePtrInfoValid,
tf,
std::string(negation ? "isn't" : "is") + " " +
std::string(tf ? "valid" : "invalid")) {
return tf == arg->is_valid();
}
ACTION_P(QuitLoop, run_loop) {
run_loop->Quit();
}
ACTION_TEMPLATE(MoveArgPointee,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(out)) {
*out = std::move(*::testing::get<k>(args));
}
base::FilePath CreateAndReturnTempDir(base::ScopedTempDir* temp_dir) {
CHECK(temp_dir->CreateUniqueTempDir());
return temp_dir->GetPath();
}
storage::BucketInfo ToBucketInfo(const BucketLocator& bucket_locator) {
storage::BucketInfo bucket_info;
bucket_info.id = bucket_locator.id;
bucket_info.storage_key = bucket_locator.storage_key;
bucket_info.name = storage::kDefaultBucketName;
return bucket_info;
}
struct TestDatabaseConnection {
TestDatabaseConnection() = default;
TestDatabaseConnection(scoped_refptr<base::SequencedTaskRunner> task_runner,
url::Origin origin,
std::u16string db_name,
int64_t version,
int64_t upgrade_txn_id)
: task_runner(std::move(task_runner)),
origin(std::move(origin)),
db_name(std::move(db_name)),
version(version),
upgrade_txn_id(upgrade_txn_id),
open_callbacks(std::make_unique<StrictMock<MockMojoFactoryClient>>()),
connection_callbacks(
std::make_unique<StrictMock<MockMojoDatabaseCallbacks>>()) {}
TestDatabaseConnection(const TestDatabaseConnection&) = delete;
TestDatabaseConnection& operator=(const TestDatabaseConnection&) = delete;
TestDatabaseConnection(TestDatabaseConnection&&) noexcept = default;
TestDatabaseConnection& operator=(TestDatabaseConnection&&) noexcept =
default;
~TestDatabaseConnection() = default;
void Open(blink::mojom::IDBFactory* factory) {
factory->Open(
open_callbacks->CreateInterfacePtrAndBind(),
connection_callbacks->CreateInterfacePtrAndBind(), db_name, version,
version_change_transaction.BindNewEndpointAndPassReceiver(task_runner),
upgrade_txn_id, 0);
EXPECT_CALL(*connection_callbacks, ForcedClose())
.Times(testing::AnyNumber());
}
scoped_refptr<base::SequencedTaskRunner> task_runner;
url::Origin origin;
std::u16string db_name;
int64_t version;
int64_t upgrade_txn_id;
mojo::AssociatedRemote<blink::mojom::IDBDatabase> database;
mojo::AssociatedRemote<blink::mojom::IDBTransaction>
version_change_transaction;
std::unique_ptr<MockMojoFactoryClient> open_callbacks;
std::unique_ptr<MockMojoDatabaseCallbacks> connection_callbacks;
};
class TestIndexedDBObserver : public storage::mojom::IndexedDBObserver {
public:
explicit TestIndexedDBObserver(
mojo::PendingReceiver<storage::mojom::IndexedDBObserver> receiver)
: receiver_(this, std::move(receiver)) {}
void OnIndexedDBListChanged(const BucketLocator& bucket_locator) override {
++notify_list_changed_count;
}
void OnIndexedDBContentChanged(
const BucketLocator& bucket_locator,
const std::u16string& database_name,
const std::u16string& object_store_name) override {
++notify_content_changed_count;
}
int notify_list_changed_count = 0;
int notify_content_changed_count = 0;
private:
mojo::Receiver<storage::mojom::IndexedDBObserver> receiver_;
};
}
class IndexedDBTest : public testing::Test,
public testing::WithParamInterface<bool> {
public:
blink::StorageKey kNormalFirstPartyStorageKey;
BucketLocator kNormalFirstPartyBucketLocator;
blink::StorageKey kSessionOnlyFirstPartyStorageKey;
BucketLocator kSessionOnlyFirstPartyBucketLocator;
blink::StorageKey kSessionOnlySubdomainFirstPartyStorageKey;
BucketLocator kSessionOnlySubdomainFirstPartyBucketLocator;
blink::StorageKey kNormalThirdPartyStorageKey;
BucketLocator kNormalThirdPartyBucketLocator;
blink::StorageKey kSessionOnlyThirdPartyStorageKey;
BucketLocator kSessionOnlyThirdPartyBucketLocator;
blink::StorageKey kSessionOnlySubdomainThirdPartyStorageKey;
BucketLocator kSessionOnlySubdomainThirdPartyBucketLocator;
blink::StorageKey kInvertedNormalThirdPartyStorageKey;
BucketLocator kInvertedNormalThirdPartyBucketLocator;
blink::StorageKey kInvertedSessionOnlyThirdPartyStorageKey;
BucketLocator kInvertedSessionOnlyThirdPartyBucketLocator;
blink::StorageKey kInvertedSessionOnlySubdomainThirdPartyStorageKey;
BucketLocator kInvertedSessionOnlySubdomainThirdPartyBucketLocator;
IndexedDBTest()
: sqlite_override_(BucketContext::OverrideShouldUseSqliteForTesting(
IsSqliteBackingStoreEnabled())),
special_storage_policy_(
base::MakeRefCounted<storage::MockSpecialStoragePolicy>()),
quota_manager_(base::MakeRefCounted<storage::MockQuotaManager>(
false,
CreateAndReturnTempDir(&temp_dir_),
base::SingleThreadTaskRunner::GetCurrentDefault(),
special_storage_policy_)),
quota_manager_proxy_(
base::MakeRefCounted<storage::MockQuotaManagerProxy>(
quota_manager_.get(),
base::SequencedTaskRunner::GetCurrentDefault())) {
mojo::PendingRemote<storage::mojom::BlobStorageContext>
pending_blob_storage_context;
blob_storage_context_.Clone(
pending_blob_storage_context.InitWithNewPipeAndPassReceiver());
context_ = std::make_unique<IndexedDBContextImpl>(
temp_dir_.GetPath(), quota_manager_proxy_.get(),
std::move(pending_blob_storage_context),
mojo::NullRemote(),
base::SequencedTaskRunner::GetCurrentDefault());
RunPostedTasks();
kNormalFirstPartyStorageKey =
blink::StorageKey::CreateFromStringForTesting("http://normal.com/");
storage::BucketInfo bucket_info = InitBucket(kNormalFirstPartyStorageKey);
kNormalFirstPartyBucketLocator = bucket_info.ToBucketLocator();
kSessionOnlyFirstPartyStorageKey =
blink::StorageKey::CreateFromStringForTesting(
"http://session-only.com/");
bucket_info = InitBucket(kSessionOnlyFirstPartyStorageKey);
kSessionOnlyFirstPartyBucketLocator = bucket_info.ToBucketLocator();
kSessionOnlySubdomainFirstPartyStorageKey =
blink::StorageKey::CreateFromStringForTesting(
"http://subdomain.session-only.com/");
bucket_info = InitBucket(kSessionOnlySubdomainFirstPartyStorageKey);
kSessionOnlySubdomainFirstPartyBucketLocator =
bucket_info.ToBucketLocator();
kNormalThirdPartyStorageKey = blink::StorageKey::Create(
url::Origin::Create(GURL("http://normal.com/")),
net::SchemefulSite(GURL("http://rando.com/")),
blink::mojom::AncestorChainBit::kCrossSite);
bucket_info = InitBucket(kNormalThirdPartyStorageKey);
kNormalThirdPartyBucketLocator = bucket_info.ToBucketLocator();
kSessionOnlyThirdPartyStorageKey = blink::StorageKey::Create(
url::Origin::Create(GURL("http://session-only.com/")),
net::SchemefulSite(GURL("http://rando.com/")),
blink::mojom::AncestorChainBit::kCrossSite);
bucket_info = InitBucket(kSessionOnlyThirdPartyStorageKey);
kSessionOnlyThirdPartyBucketLocator = bucket_info.ToBucketLocator();
kSessionOnlySubdomainThirdPartyStorageKey = blink::StorageKey::Create(
url::Origin::Create(GURL("http://subdomain.session-only.com/")),
net::SchemefulSite(GURL("http://rando.com/")),
blink::mojom::AncestorChainBit::kCrossSite);
bucket_info = InitBucket(kSessionOnlySubdomainThirdPartyStorageKey);
kSessionOnlySubdomainThirdPartyBucketLocator =
bucket_info.ToBucketLocator();
kInvertedNormalThirdPartyStorageKey = blink::StorageKey::Create(
url::Origin::Create(GURL("http://rando.com/")),
net::SchemefulSite(GURL("http://normal.com/")),
blink::mojom::AncestorChainBit::kCrossSite);
bucket_info = InitBucket(kInvertedNormalThirdPartyStorageKey);
kInvertedNormalThirdPartyBucketLocator = bucket_info.ToBucketLocator();
kInvertedSessionOnlyThirdPartyStorageKey = blink::StorageKey::Create(
url::Origin::Create(GURL("http://rando.com/")),
net::SchemefulSite(GURL("http://session-only.com/")),
blink::mojom::AncestorChainBit::kCrossSite);
bucket_info = InitBucket(kInvertedSessionOnlyThirdPartyStorageKey);
kInvertedSessionOnlyThirdPartyBucketLocator = bucket_info.ToBucketLocator();
kInvertedSessionOnlySubdomainThirdPartyStorageKey =
blink::StorageKey::Create(
url::Origin::Create(GURL("http://rando.com/")),
net::SchemefulSite(GURL("http://subdomain.session-only.com/")),
blink::mojom::AncestorChainBit::kCrossSite);
bucket_info = InitBucket(kInvertedSessionOnlySubdomainThirdPartyStorageKey);
kInvertedSessionOnlySubdomainThirdPartyBucketLocator =
bucket_info.ToBucketLocator();
}
IndexedDBTest(const IndexedDBTest&) = delete;
IndexedDBTest& operator=(const IndexedDBTest&) = delete;
~IndexedDBTest() override = default;
bool IsSqliteBackingStoreEnabled() { return GetParam(); }
storage::BucketInfo InitBucket(const blink::StorageKey& storage_key) {
storage::BucketInfo bucket;
quota_manager_->UpdateOrCreateBucket(
storage::BucketInitParams::ForDefaultBucket(storage_key),
base::BindOnce(
[](storage::BucketInfo* info,
storage::QuotaErrorOr<storage::BucketInfo> bucket_info) {
*info = bucket_info.value();
},
&bucket));
return bucket;
}
void SetUpInMemoryContext() {
mojo::PendingRemote<storage::mojom::BlobStorageContext>
pending_blob_storage_context;
blob_storage_context_.Clone(
pending_blob_storage_context.InitWithNewPipeAndPassReceiver());
context_ = std::make_unique<IndexedDBContextImpl>(
base::FilePath(), quota_manager_proxy_.get(),
std::move(pending_blob_storage_context),
mojo::NullRemote(),
base::SequencedTaskRunner::GetCurrentDefault());
RunPostedTasks();
}
void RunPostedTasks() {
base::RunLoop loop;
context_->idb_task_runner()->PostTask(FROM_HERE, loop.QuitClosure());
loop.Run();
}
void SetUp() override { ResetGlobalSweepAndCompactionTimesForTest(); }
void TearDown() override {
factory_remote_.reset();
if (context_ && !context_->in_memory()) {
std::set<BucketLocator> buckets = context_->bucket_set_;
for (const BucketLocator& bucket_locator : buckets) {
context_->DeleteBucketData(bucket_locator, base::DoNothing());
}
while (!context_->GetOpenBucketIdsForTesting().empty()) {
RunPostedTasks();
}
}
if (temp_dir_.IsValid()) {
ASSERT_TRUE(temp_dir_.Delete());
}
}
base::FilePath GetFilePathForTesting(const BucketLocator& bucket_locator) {
return context()->GetFilePathForTesting(bucket_locator,
IsSqliteBackingStoreEnabled());
}
bool IsThirdPartyStoragePartitioningEnabled() {
return base::FeatureList::IsEnabled(
net::features::kThirdPartyStoragePartitioning);
}
bool DeleteBucket(const storage::BucketInfo* bucket_info) {
base::test::TestFuture<blink::mojom::QuotaStatusCode> result_code;
context()->DeleteBucketData(bucket_info->ToBucketLocator(),
result_code.GetCallback());
return result_code.Get() == blink::mojom::QuotaStatusCode::kOk;
}
void BindFactory(
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote,
mojo::PendingReceiver<blink::mojom::IDBFactory> receiver,
storage::QuotaErrorOr<storage::BucketInfo> bucket_info) {
context()->BindIndexedDBImpl(storage::BucketClientInfo{},
std::move(checker_remote), std::move(receiver),
bucket_info);
}
blink::StorageKey GetTestStorageKey() {
return blink::StorageKey::CreateFromStringForTesting("http://test/");
}
void VerifyForcedClosedCalled(base::OnceClosure action,
storage::BucketInfo* out_info = nullptr) {
storage::BucketInfo bucket_info = InitBucket(GetTestStorageKey());
if (out_info) {
*out_info = bucket_info;
}
BucketLocator bucket_locator = bucket_info.ToBucketLocator();
base::FilePath test_path = GetFilePathForTesting(bucket_locator);
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote_.BindNewPipeAndPassReceiver(), bucket_info);
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
base::RunLoop run_loop;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
EXPECT_CALL(client, MockedUpgradeNeeded)
.WillOnce(
testing::DoAll(MoveArgPointee<0>(&pending_database),
::base::test::RunClosure(run_loop.QuitClosure())));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote_->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(),
u"opendb",
blink::IndexedDBDatabaseMetadata::NO_VERSION,
transaction_remote.BindNewEndpointAndPassReceiver(),
0, 0);
run_loop.Run();
EXPECT_TRUE(base::DirectoryExists(test_path));
base::RunLoop run_loop2;
EXPECT_CALL(database_callbacks, ForcedClose())
.WillOnce(::base::test::RunClosure(run_loop2.QuitClosure()));
std::move(action).Run();
run_loop2.Run();
}
BucketContext& GetOrCreateBucketContext(
const storage::BucketInfo& bucket,
const base::FilePath& data_directory) {
context_->EnsureBucketContext(bucket, data_directory);
return *GetBucketContext(bucket.id);
}
BucketContext* GetBucketContext(storage::BucketId id) {
auto* sequence_bound = context_->GetBucketContextForTesting(id);
if (!sequence_bound) {
return nullptr;
}
base::test::TestFuture<BucketContext*> future;
sequence_bound->AsyncCall(&BucketContext::GetReferenceForTesting)
.Then(future.GetCallback());
return future.Get();
}
storage::BucketInfo GetOrCreateBucket(
const storage::BucketInitParams& params) {
base::test::TestFuture<storage::QuotaErrorOr<storage::BucketInfo>> future;
quota_manager_proxy_->UpdateOrCreateBucket(
params, base::SingleThreadTaskRunner::GetCurrentDefault(),
future.GetCallback());
return future.Take().value();
}
BucketContextHandle CreateBucketHandle(
std::optional<storage::BucketLocator> bucket_locator = std::nullopt) {
if (!bucket_locator) {
const blink::StorageKey storage_key =
blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
bucket_locator = BucketLocator();
bucket_locator->storage_key = storage_key;
}
context_->EnsureBucketContext(ToBucketInfo(*bucket_locator),
context()->GetDataPath(*bucket_locator));
BucketContextHandle bucket_context_handle(
*GetBucketContext(bucket_locator->id));
bucket_context_handle->InitBackingStore(
true);
return bucket_context_handle;
}
void VerifyBucketContextWaitIfNeeded(const storage::BucketId& id,
bool expected_context_exists) {
while (expected_context_exists != context_->BucketContextExists(id)) {
RunPostedTasks();
}
VerifyBucketContext(id, expected_context_exists);
}
void VerifyBucketContext(
const storage::BucketId& id,
bool expected_context_exists,
std::optional<bool> expected_backing_store_exists = std::nullopt) {
BucketContext* context = GetBucketContext(id);
if (!expected_context_exists) {
EXPECT_FALSE(context);
EXPECT_FALSE(expected_backing_store_exists.has_value());
} else {
ASSERT_TRUE(context);
if (expected_backing_store_exists.has_value()) {
EXPECT_EQ(*expected_backing_store_exists, !!context->backing_store());
}
}
}
protected:
IndexedDBContextImpl* context() const { return context_.get(); }
base::AutoReset<std::optional<bool>> sqlite_override_;
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::ScopedTempDir temp_dir_;
scoped_refptr<storage::MockSpecialStoragePolicy> special_storage_policy_;
scoped_refptr<storage::MockQuotaManager> quota_manager_;
scoped_refptr<storage::MockQuotaManagerProxy> quota_manager_proxy_;
MockBlobStorageContext blob_storage_context_;
std::unique_ptr<IndexedDBContextImpl> context_;
mojo::Remote<blink::mojom::IDBFactory> factory_remote_;
};
INSTANTIATE_TEST_SUITE_P(
,
IndexedDBTest,
testing::Bool(),
[](const testing::TestParamInfo<IndexedDBTest::ParamType>& info) {
return info.param ? "SQLite" : "LevelDB";
});
TEST_P(IndexedDBTest, CloseConnectionBeforeUpgrade) {
const int64_t kDBVersion = 1;
const int64_t kTransactionId = 1;
std::unique_ptr<TestDatabaseConnection> connection;
IndexedDBDatabaseMetadata metadata;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
mojo::Remote<blink::mojom::IDBFactory> bounded_factory_remote;
BindFactory(std::move(checker_remote),
bounded_factory_remote.BindNewPipeAndPassReceiver(),
storage::BucketInfo());
base::RunLoop loop;
connection = std::make_unique<TestDatabaseConnection>(
context()->idb_task_runner(), ToOrigin(kOrigin), kDatabaseName,
kDBVersion, kTransactionId);
EXPECT_CALL(
*connection->open_callbacks,
MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
IndexedDBDatabaseMetadata::NO_VERSION,
blink::mojom::IDBDataLoss::None, std::string(""), _))
.WillOnce(testing::DoAll(MoveArgPointee<0>(&pending_database),
testing::SaveArg<4>(&metadata),
QuitLoop(&loop)));
connection->Open(bounded_factory_remote.get());
loop.Run();
EXPECT_TRUE(pending_database.is_valid());
EXPECT_EQ(connection->version, metadata.version);
EXPECT_EQ(connection->db_name, metadata.name);
connection.reset();
}
TEST_P(IndexedDBTest, CloseAfterUpgrade) {
const int64_t kDBVersion = 1;
const int64_t kTransactionId = 1;
const int64_t kObjectStoreId = 10;
const char16_t kObjectStoreName[] = u"os";
std::unique_ptr<TestDatabaseConnection> connection;
IndexedDBDatabaseMetadata metadata;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
mojo::Remote<blink::mojom::IDBFactory> bounded_factory_remote;
BindFactory(std::move(checker_remote),
bounded_factory_remote.BindNewPipeAndPassReceiver(),
storage::BucketInfo());
base::RunLoop loop;
connection = std::make_unique<TestDatabaseConnection>(
context()->idb_task_runner(), ToOrigin(kOrigin), kDatabaseName,
kDBVersion, kTransactionId);
EXPECT_CALL(
*connection->open_callbacks,
MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
IndexedDBDatabaseMetadata::NO_VERSION,
blink::mojom::IDBDataLoss::None, std::string(""), _))
.WillOnce(testing::DoAll(MoveArgPointee<0>(&pending_database),
testing::SaveArg<4>(&metadata),
QuitLoop(&loop)));
connection->Open(bounded_factory_remote.get());
loop.Run();
ASSERT_TRUE(pending_database.is_valid());
EXPECT_EQ(connection->version, metadata.version);
EXPECT_EQ(connection->db_name, metadata.name);
base::RunLoop loop2;
base::RepeatingClosure quit_closure2 =
base::BarrierClosure(2, loop2.QuitClosure());
::testing::InSequence dummy;
EXPECT_CALL(*connection->connection_callbacks, Complete(kTransactionId))
.Times(1)
.WillOnce(RunClosure(quit_closure2));
EXPECT_CALL(*connection->open_callbacks,
MockedOpenSuccess(IsAssociatedInterfacePtrInfoValid(false), _))
.Times(1)
.WillOnce(RunClosure(std::move(quit_closure2)));
connection->database.Bind(std::move(pending_database));
ASSERT_TRUE(connection->database.is_bound());
ASSERT_TRUE(connection->version_change_transaction.is_bound());
connection->version_change_transaction->CreateObjectStore(
kObjectStoreId, kObjectStoreName, blink::IndexedDBKeyPath(), false);
connection->version_change_transaction->Commit(0);
loop2.Run();
connection.reset();
}
#if BUILDFLAG(IS_MAC) && !defined(NDEBUG)
#define MAYBE_OpenNewConnectionWhileUpgrading \
DISABLED_OpenNewConnectionWhileUpgrading
#else
#define MAYBE_OpenNewConnectionWhileUpgrading OpenNewConnectionWhileUpgrading
#endif
TEST_P(IndexedDBTest, MAYBE_OpenNewConnectionWhileUpgrading) {
const int64_t kDBVersion = 1;
const int64_t kTransactionId = 1;
const int64_t kObjectStoreId = 10;
const char16_t kObjectStoreName[] = u"os";
std::unique_ptr<TestDatabaseConnection> connection1;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database1;
IndexedDBDatabaseMetadata metadata1;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
mojo::Remote<blink::mojom::IDBFactory> bounded_factory_remote;
BindFactory(std::move(checker_remote),
bounded_factory_remote.BindNewPipeAndPassReceiver(),
storage::BucketInfo());
base::RunLoop loop;
connection1 = std::make_unique<TestDatabaseConnection>(
context()->idb_task_runner(), ToOrigin(kOrigin), kDatabaseName,
kDBVersion, kTransactionId);
EXPECT_CALL(
*connection1->open_callbacks,
MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
IndexedDBDatabaseMetadata::NO_VERSION,
blink::mojom::IDBDataLoss::None, std::string(""), _))
.WillOnce(testing::DoAll(MoveArgPointee<0>(&pending_database1),
testing::SaveArg<4>(&metadata1),
QuitLoop(&loop)));
connection1->Open(bounded_factory_remote.get());
loop.Run();
std::unique_ptr<TestDatabaseConnection> connection2;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database2;
IndexedDBDatabaseMetadata metadata2;
base::RunLoop loop2;
base::RepeatingClosure quit_closure2 =
base::BarrierClosure(3, loop2.QuitClosure());
connection2 = std::make_unique<TestDatabaseConnection>(
context()->idb_task_runner(), ToOrigin(kOrigin), kDatabaseName,
kDBVersion, 0);
::testing::InSequence dummy;
EXPECT_CALL(*connection1->connection_callbacks, Complete(kTransactionId))
.Times(1)
.WillOnce(RunClosure(quit_closure2));
EXPECT_CALL(*connection1->open_callbacks,
MockedOpenSuccess(IsAssociatedInterfacePtrInfoValid(false), _))
.Times(1)
.WillOnce(RunClosure(quit_closure2));
EXPECT_CALL(*connection2->open_callbacks,
MockedOpenSuccess(IsAssociatedInterfacePtrInfoValid(true), _))
.WillOnce(testing::DoAll(MoveArgPointee<0>(&pending_database2),
testing::SaveArg<1>(&metadata2),
RunClosure(std::move(quit_closure2))));
connection1->database.Bind(std::move(pending_database1));
ASSERT_TRUE(connection1->database.is_bound());
ASSERT_TRUE(connection1->version_change_transaction.is_bound());
connection2->Open(bounded_factory_remote.get());
connection1->version_change_transaction->CreateObjectStore(
kObjectStoreId, kObjectStoreName, blink::IndexedDBKeyPath(), false);
connection1->version_change_transaction->Commit(0);
loop2.Run();
EXPECT_TRUE(pending_database2.is_valid());
EXPECT_EQ(connection2->version, metadata2.version);
EXPECT_EQ(connection2->db_name, metadata2.name);
connection1.reset();
connection2.reset();
}
MATCHER_P(IsCallbackError, error_code, "") {
if (arg->is_error_result() &&
arg->get_error_result()->error_code == error_code) {
return true;
}
return false;
}
TEST_P(IndexedDBTest, DISABLED_PutWithInvalidBlob) {
const int64_t kDBVersion = 1;
const int64_t kTransactionId = 1;
const int64_t kObjectStoreId = 10;
const char16_t kObjectStoreName[] = u"os";
std::unique_ptr<TestDatabaseConnection> connection;
IndexedDBDatabaseMetadata metadata;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
mojo::Remote<blink::mojom::IDBFactory> bounded_factory_remote;
BindFactory(std::move(checker_remote),
bounded_factory_remote.BindNewPipeAndPassReceiver(),
storage::BucketInfo());
base::RunLoop loop;
connection = std::make_unique<TestDatabaseConnection>(
context()->idb_task_runner(), ToOrigin(kOrigin), kDatabaseName,
kDBVersion, kTransactionId);
EXPECT_CALL(
*connection->open_callbacks,
MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
IndexedDBDatabaseMetadata::NO_VERSION,
blink::mojom::IDBDataLoss::None, std::string(""), _))
.WillOnce(testing::DoAll(MoveArgPointee<0>(&pending_database),
testing::SaveArg<4>(&metadata),
QuitLoop(&loop)));
connection->Open(bounded_factory_remote.get());
loop.Run();
ASSERT_TRUE(pending_database.is_valid());
EXPECT_EQ(connection->version, metadata.version);
EXPECT_EQ(connection->db_name, metadata.name);
base::MockCallback<blink::mojom::IDBTransaction::PutCallback> put_callback;
base::RunLoop loop2;
base::RepeatingClosure quit_closure2 =
base::BarrierClosure(3, loop2.QuitClosure());
::testing::InSequence dummy;
EXPECT_CALL(put_callback,
Run(IsCallbackError(blink::mojom::IDBException::kUnknownError)))
.Times(1)
.WillOnce(RunClosure(quit_closure2));
EXPECT_CALL(
*connection->connection_callbacks,
Abort(kTransactionId, blink::mojom::IDBException::kUnknownError, _))
.Times(1)
.WillOnce(RunClosure(quit_closure2));
EXPECT_CALL(*connection->open_callbacks,
Error(blink::mojom::IDBException::kAbortError, _))
.Times(1)
.WillOnce(RunClosure(std::move(quit_closure2)));
connection->database.Bind(std::move(pending_database));
ASSERT_TRUE(connection->database.is_bound());
ASSERT_TRUE(connection->version_change_transaction.is_bound());
connection->version_change_transaction->CreateObjectStore(
kObjectStoreId, kObjectStoreName, blink::IndexedDBKeyPath(), false);
std::vector<blink::mojom::IDBExternalObjectPtr> external_objects;
mojo::PendingRemote<blink::mojom::Blob> blob;
std::ignore = blob.InitWithNewPipeAndPassReceiver();
external_objects.push_back(blink::mojom::IDBExternalObject::NewBlobOrFile(
blink::mojom::IDBBlobInfo::New(std::move(blob), std::u16string(), 100,
nullptr)));
auto new_value = blink::mojom::IDBValue::New();
new_value->bits = mojo_base::BigBuffer(base::as_byte_span("hello"));
new_value->external_objects = std::move(external_objects);
connection->version_change_transaction->Put(
kObjectStoreId, std::move(new_value), IndexedDBKey(u"hello"),
blink::mojom::IDBPutMode::AddOnly, std::vector<IndexedDBIndexKeys>(),
put_callback.Get());
connection->version_change_transaction->Commit(0);
loop2.Run();
connection.reset();
}
TEST_P(IndexedDBTest, InvalidObjectStoreId) {
const int64_t kDBVersion = 1;
const int64_t kTransactionId = 1;
const int64_t kObjectStoreId = 10;
const int64_t kIndexId = 100;
const char16_t kObjectStoreName[] = u"os";
const char16_t kIndexName[] = u"index";
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
mojo::Remote<blink::mojom::IDBFactory> bounded_factory_remote;
BucketContextHandle bucket_context_handle = CreateBucketHandle();
const BucketLocator& bucket_locator = bucket_context_handle->bucket_locator();
BindFactory(std::move(checker_remote),
bounded_factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
std::unique_ptr<TestDatabaseConnection> connection;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
{
base::RunLoop loop;
connection = std::make_unique<TestDatabaseConnection>(
context()->idb_task_runner(), ToOrigin(kOrigin), kDatabaseName,
kDBVersion, kTransactionId);
EXPECT_CALL(
*connection->open_callbacks,
MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
IndexedDBDatabaseMetadata::NO_VERSION,
blink::mojom::IDBDataLoss::None, std::string(), _))
.WillOnce(testing::DoAll(MoveArgPointee<0>(&pending_database),
QuitLoop(&loop)));
connection->Open(bounded_factory_remote.get());
loop.Run();
}
EXPECT_TRUE(pending_database.is_valid());
{
mojo::test::BadMessageObserver bad_message_observer;
connection->database.Bind(std::move(pending_database));
ASSERT_TRUE(connection->database.is_bound());
ASSERT_TRUE(connection->version_change_transaction.is_bound());
ASSERT_TRUE(connection->database.is_bound());
connection->version_change_transaction->CreateObjectStore(
kObjectStoreId, kObjectStoreName, blink::IndexedDBKeyPath(), false);
connection->database->CreateIndex(
kTransactionId, kObjectStoreId + 123,
blink::IndexedDBIndexMetadata(kIndexName, kIndexId,
blink::IndexedDBKeyPath(), false, false));
EXPECT_EQ("Invalid object_store_id or index_id.",
bad_message_observer.WaitForBadMessage());
}
}
TEST_P(IndexedDBTest, NotifyIndexedDBListChanged) {
const int64_t kDBVersion1 = 1;
const int64_t kDBVersion2 = 2;
const int64_t kDBVersion3 = 3;
const int64_t kTransactionId1 = 1;
const int64_t kTransactionId2 = 2;
const int64_t kTransactionId3 = 3;
const int64_t kObjectStoreId = 10;
const int64_t kIndexId = 100;
const char16_t kObjectStoreName[] = u"os";
const char16_t kIndexName[] = u"index";
mojo::PendingReceiver<storage::mojom::IndexedDBObserver> receiver;
mojo::PendingRemote<storage::mojom::IndexedDBObserver> remote;
TestIndexedDBObserver observer(remote.InitWithNewPipeAndPassReceiver());
context()->AddObserver(std::move(remote));
EXPECT_EQ(0, observer.notify_list_changed_count);
EXPECT_EQ(0, observer.notify_content_changed_count);
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
mojo::Remote<blink::mojom::IDBFactory> bounded_factory_remote;
BucketContextHandle bucket_context_handle = CreateBucketHandle();
const BucketLocator& bucket_locator = bucket_context_handle->bucket_locator();
BindFactory(std::move(checker_remote),
bounded_factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
std::unique_ptr<TestDatabaseConnection> connection1;
IndexedDBDatabaseMetadata metadata1;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database1;
EXPECT_EQ(0, observer.notify_list_changed_count);
{
base::RunLoop loop;
connection1 = std::make_unique<TestDatabaseConnection>(
context()->idb_task_runner(), ToOrigin(kOrigin), kDatabaseName,
kDBVersion1, kTransactionId1);
EXPECT_CALL(
*connection1->open_callbacks,
MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
IndexedDBDatabaseMetadata::NO_VERSION,
blink::mojom::IDBDataLoss::None, std::string(), _))
.WillOnce(testing::DoAll(MoveArgPointee<0>(&pending_database1),
testing::SaveArg<4>(&metadata1),
QuitLoop(&loop)));
connection1->Open(bounded_factory_remote.get());
loop.Run();
}
EXPECT_TRUE(pending_database1.is_valid());
EXPECT_EQ(connection1->version, metadata1.version);
EXPECT_EQ(connection1->db_name, metadata1.name);
{
::testing::InSequence dummy;
base::RunLoop loop;
base::RepeatingClosure quit_closure =
base::BarrierClosure(2, loop.QuitClosure());
connection1->database.Bind(std::move(pending_database1));
ASSERT_TRUE(connection1->database.is_bound());
ASSERT_TRUE(connection1->version_change_transaction.is_bound());
EXPECT_CALL(*connection1->connection_callbacks, Complete(kTransactionId1))
.Times(1)
.WillOnce(RunClosure(quit_closure));
EXPECT_CALL(*connection1->open_callbacks,
MockedOpenSuccess(IsAssociatedInterfacePtrInfoValid(false), _))
.Times(1)
.WillOnce(RunClosure(std::move(quit_closure)));
ASSERT_TRUE(connection1->database.is_bound());
connection1->version_change_transaction->CreateObjectStore(
kObjectStoreId, kObjectStoreName, blink::IndexedDBKeyPath(), false);
connection1->database->CreateIndex(
kTransactionId1, kObjectStoreId,
blink::IndexedDBIndexMetadata(kIndexName, kIndexId,
blink::IndexedDBKeyPath(), false, false));
connection1->version_change_transaction->Commit(0);
loop.Run();
}
EXPECT_EQ(1, observer.notify_list_changed_count);
connection1.reset();
std::unique_ptr<TestDatabaseConnection> connection2;
IndexedDBDatabaseMetadata metadata2;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database2;
{
::testing::InSequence dummy;
base::RunLoop loop;
base::RepeatingClosure quit_closure =
base::BarrierClosure(2, loop.QuitClosure());
connection2 = std::make_unique<TestDatabaseConnection>(
context()->idb_task_runner(), ToOrigin(kOrigin), kDatabaseName,
kDBVersion2, kTransactionId2);
EXPECT_CALL(*connection2->open_callbacks,
MockedUpgradeNeeded(
IsAssociatedInterfacePtrInfoValid(true), kDBVersion1,
blink::mojom::IDBDataLoss::None, std::string(), _))
.WillOnce(testing::DoAll(MoveArgPointee<0>(&pending_database2),
testing::SaveArg<4>(&metadata2),
QuitLoop(&loop)));
connection2->Open(bounded_factory_remote.get());
loop.Run();
}
EXPECT_TRUE(pending_database2.is_valid());
EXPECT_EQ(connection2->version, metadata2.version);
EXPECT_EQ(connection2->db_name, metadata2.name);
{
::testing::InSequence dummy;
base::RunLoop loop;
base::RepeatingClosure quit_closure =
base::BarrierClosure(2, loop.QuitClosure());
connection2->database.Bind(std::move(pending_database2));
ASSERT_TRUE(connection2->database.is_bound());
ASSERT_TRUE(connection2->version_change_transaction.is_bound());
EXPECT_CALL(*connection2->connection_callbacks, Complete(kTransactionId2))
.Times(1)
.WillOnce(RunClosure(quit_closure));
EXPECT_CALL(*connection2->open_callbacks,
MockedOpenSuccess(IsAssociatedInterfacePtrInfoValid(false), _))
.Times(1)
.WillOnce(RunClosure(std::move(quit_closure)));
ASSERT_TRUE(connection2->database.is_bound());
connection2->database->DeleteIndex(kTransactionId2, kObjectStoreId,
kIndexId);
connection2->version_change_transaction->Commit(0);
loop.Run();
}
EXPECT_EQ(2, observer.notify_list_changed_count);
connection2.reset();
std::unique_ptr<TestDatabaseConnection> connection3;
IndexedDBDatabaseMetadata metadata3;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database3;
{
::testing::InSequence dummy;
base::RunLoop loop;
connection3 = std::make_unique<TestDatabaseConnection>(
context()->idb_task_runner(), ToOrigin(kOrigin), kDatabaseName,
kDBVersion3, kTransactionId3);
EXPECT_CALL(*connection3->open_callbacks,
MockedUpgradeNeeded(
IsAssociatedInterfacePtrInfoValid(true), kDBVersion2,
blink::mojom::IDBDataLoss::None, std::string(), _))
.WillOnce(testing::DoAll(MoveArgPointee<0>(&pending_database3),
testing::SaveArg<4>(&metadata3),
QuitLoop(&loop)));
connection3->Open(bounded_factory_remote.get());
loop.Run();
}
EXPECT_TRUE(pending_database3.is_valid());
EXPECT_EQ(connection3->version, metadata3.version);
EXPECT_EQ(connection3->db_name, metadata3.name);
{
::testing::InSequence dummy;
base::RunLoop loop;
base::RepeatingClosure quit_closure =
base::BarrierClosure(2, loop.QuitClosure());
connection3->database.Bind(std::move(pending_database3));
ASSERT_TRUE(connection3->database.is_bound());
ASSERT_TRUE(connection3->version_change_transaction.is_bound());
EXPECT_CALL(*connection3->connection_callbacks, Complete(kTransactionId3))
.Times(1)
.WillOnce(RunClosure(quit_closure));
EXPECT_CALL(*connection3->open_callbacks,
MockedOpenSuccess(IsAssociatedInterfacePtrInfoValid(false), _))
.Times(1)
.WillOnce(RunClosure(std::move(quit_closure)));
ASSERT_TRUE(connection3->database.is_bound());
connection3->version_change_transaction->DeleteObjectStore(kObjectStoreId);
connection3->version_change_transaction->Commit(0);
loop.Run();
}
EXPECT_EQ(3, observer.notify_list_changed_count);
connection3.reset();
}
MATCHER(IsSuccessKey, "") {
return arg->is_key();
}
TEST_P(IndexedDBTest, NotifyIndexedDBContentChanged) {
const int64_t kDBVersion1 = 1;
const int64_t kDBVersion2 = 2;
const int64_t kTransactionId1 = 1;
const int64_t kTransactionId2 = 2;
const int64_t kObjectStoreId = 10;
const char16_t kObjectStoreName[] = u"os";
mojo::PendingReceiver<storage::mojom::IndexedDBObserver> receiver;
mojo::PendingRemote<storage::mojom::IndexedDBObserver> remote;
TestIndexedDBObserver observer(remote.InitWithNewPipeAndPassReceiver());
context()->AddObserver(std::move(remote));
EXPECT_EQ(0, observer.notify_list_changed_count);
EXPECT_EQ(0, observer.notify_content_changed_count);
std::unique_ptr<TestDatabaseConnection> connection1;
IndexedDBDatabaseMetadata metadata1;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database1;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
mojo::Remote<blink::mojom::IDBFactory> bounded_factory_remote;
BucketContextHandle bucket_context_handle = CreateBucketHandle();
const BucketLocator& bucket_locator = bucket_context_handle->bucket_locator();
BindFactory(std::move(checker_remote),
bounded_factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
base::RunLoop loop;
connection1 = std::make_unique<TestDatabaseConnection>(
context()->idb_task_runner(), ToOrigin(kOrigin), kDatabaseName,
kDBVersion1, kTransactionId1);
EXPECT_CALL(
*connection1->open_callbacks,
MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
IndexedDBDatabaseMetadata::NO_VERSION,
blink::mojom::IDBDataLoss::None, std::string(), _))
.WillOnce(testing::DoAll(MoveArgPointee<0>(&pending_database1),
testing::SaveArg<4>(&metadata1),
QuitLoop(&loop)));
connection1->Open(bounded_factory_remote.get());
loop.Run();
EXPECT_TRUE(pending_database1.is_valid());
EXPECT_EQ(connection1->version, metadata1.version);
EXPECT_EQ(connection1->db_name, metadata1.name);
base::MockCallback<blink::mojom::IDBTransaction::PutCallback> put_callback;
base::RunLoop loop2;
base::RepeatingClosure quit_closure2 =
base::BarrierClosure(3, loop2.QuitClosure());
::testing::InSequence dummy;
EXPECT_CALL(put_callback, Run(IsSuccessKey()))
.Times(1)
.WillOnce(RunClosure(quit_closure2));
EXPECT_CALL(*connection1->connection_callbacks, Complete(kTransactionId1))
.Times(1)
.WillOnce(RunClosure(quit_closure2));
EXPECT_CALL(*connection1->open_callbacks,
MockedOpenSuccess(IsAssociatedInterfacePtrInfoValid(false), _))
.Times(1)
.WillOnce(RunClosure(std::move(quit_closure2)));
connection1->database.Bind(std::move(pending_database1));
ASSERT_TRUE(connection1->database.is_bound());
ASSERT_TRUE(connection1->version_change_transaction.is_bound());
connection1->version_change_transaction->CreateObjectStore(
kObjectStoreId, kObjectStoreName, blink::IndexedDBKeyPath(), false);
auto new_value = blink::mojom::IDBValue::New();
auto value = base::span_from_cstring("value");
new_value->bits = mojo_base::BigBuffer(base::as_bytes(value));
connection1->version_change_transaction->Put(
kObjectStoreId, std::move(new_value), IndexedDBKey(u"key"),
blink::mojom::IDBPutMode::AddOnly, std::vector<IndexedDBIndexKeys>(),
put_callback.Get());
connection1->version_change_transaction->Commit(0);
loop2.Run();
EXPECT_EQ(1, observer.notify_list_changed_count);
EXPECT_EQ(1, observer.notify_content_changed_count);
connection1.reset();
std::unique_ptr<TestDatabaseConnection> connection2;
IndexedDBDatabaseMetadata metadata2;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database2;
base::RunLoop loop4;
connection2 = std::make_unique<TestDatabaseConnection>(
context()->idb_task_runner(), ToOrigin(kOrigin), kDatabaseName,
kDBVersion2, kTransactionId2);
EXPECT_CALL(
*connection2->open_callbacks,
MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true), kDBVersion1,
blink::mojom::IDBDataLoss::None, std::string(), _))
.WillOnce(testing::DoAll(MoveArgPointee<0>(&pending_database2),
testing::SaveArg<4>(&metadata2),
QuitLoop(&loop4)));
connection2->Open(bounded_factory_remote.get());
loop4.Run();
EXPECT_TRUE(pending_database2.is_valid());
EXPECT_EQ(connection2->version, metadata2.version);
EXPECT_EQ(connection2->db_name, metadata2.name);
base::RunLoop loop5;
base::RepeatingClosure quit_closure5 =
base::BarrierClosure(3, loop5.QuitClosure());
EXPECT_CALL(*connection2->connection_callbacks, Complete(kTransactionId2))
.Times(1)
.WillOnce(RunClosure(quit_closure5));
EXPECT_CALL(*connection2->open_callbacks,
MockedOpenSuccess(IsAssociatedInterfacePtrInfoValid(false), _))
.Times(1)
.WillOnce(RunClosure(quit_closure5));
connection2->database.Bind(std::move(pending_database2));
ASSERT_TRUE(connection2->database.is_bound());
ASSERT_TRUE(connection2->version_change_transaction.is_bound());
connection2->database->Clear(kTransactionId2, kObjectStoreId,
base::IgnoreArgs<bool>(quit_closure5));
connection2->version_change_transaction->Commit(0);
loop5.Run();
EXPECT_EQ(2, observer.notify_list_changed_count);
EXPECT_EQ(2, observer.notify_content_changed_count);
connection2.reset();
}
TEST_P(IndexedDBTest, DISABLED_DatabaseOperationSequencing) {
const int64_t kDBVersion = 1;
const int64_t kTransactionId = 1;
const std::u16string kObjectStoreName1 = u"os1";
const std::u16string kObjectStoreName2 = u"os2";
const std::u16string kObjectStoreName3 = u"os3";
std::unique_ptr<TestDatabaseConnection> connection;
IndexedDBDatabaseMetadata metadata;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
mojo::Remote<blink::mojom::IDBFactory> bounded_factory_remote;
BindFactory(std::move(checker_remote),
bounded_factory_remote.BindNewPipeAndPassReceiver(),
storage::BucketInfo());
base::RunLoop loop;
connection = std::make_unique<TestDatabaseConnection>(
context()->idb_task_runner(), ToOrigin(kOrigin), kDatabaseName,
kDBVersion, kTransactionId);
EXPECT_CALL(
*connection->open_callbacks,
MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
IndexedDBDatabaseMetadata::NO_VERSION,
blink::mojom::IDBDataLoss::None, std::string(""), _))
.WillOnce(testing::DoAll(MoveArgPointee<0>(&pending_database),
testing::SaveArg<4>(&metadata),
QuitLoop(&loop)));
connection->Open(bounded_factory_remote.get());
loop.Run();
ASSERT_TRUE(pending_database.is_valid());
EXPECT_EQ(connection->version, metadata.version);
EXPECT_EQ(connection->db_name, metadata.name);
EXPECT_EQ(0ULL, metadata.object_stores.size());
IndexedDBDatabaseMetadata metadata2;
int64_t object_store_id = 1001;
base::RunLoop loop2;
base::RepeatingClosure quit_closure2 =
base::BarrierClosure(2, loop2.QuitClosure());
::testing::InSequence dummy;
EXPECT_CALL(*connection->connection_callbacks, Complete(kTransactionId))
.Times(1)
.WillOnce(RunClosure(quit_closure2));
EXPECT_CALL(*connection->open_callbacks,
MockedOpenSuccess(IsAssociatedInterfacePtrInfoValid(false), _))
.Times(1)
.WillOnce(testing::DoAll(testing::SaveArg<1>(&metadata2),
RunClosure(std::move(quit_closure2))));
connection->database.Bind(std::move(pending_database));
ASSERT_TRUE(connection->database.is_bound());
ASSERT_TRUE(connection->version_change_transaction.is_bound());
connection->version_change_transaction->CreateObjectStore(
++object_store_id, kObjectStoreName1, blink::IndexedDBKeyPath(),
false);
connection->version_change_transaction->DeleteObjectStore(object_store_id);
connection->version_change_transaction->CreateObjectStore(
++object_store_id, kObjectStoreName2, blink::IndexedDBKeyPath(),
false);
connection->version_change_transaction->DeleteObjectStore(object_store_id);
connection->version_change_transaction->CreateObjectStore(
++object_store_id, kObjectStoreName3, blink::IndexedDBKeyPath(),
false);
connection->version_change_transaction->Commit(0);
loop2.Run();
EXPECT_EQ(1ULL, metadata2.object_stores.size());
EXPECT_EQ(metadata2.object_stores[object_store_id].name, kObjectStoreName3);
connection.reset();
}
TEST_P(IndexedDBTest, ClearSessionOnlyDatabases) {
std::vector<storage::mojom::StoragePolicyUpdatePtr> policy_updates;
policy_updates.emplace_back(storage::mojom::StoragePolicyUpdate::New(
url::Origin::Create(GURL("http://subdomain.session-only.com")),
true));
context_->ApplyPolicyUpdates(std::move(policy_updates));
base::FilePath normal_path_first_party;
base::FilePath session_only_path_first_party;
base::FilePath session_only_subdomain_path_first_party;
base::FilePath normal_path_third_party;
base::FilePath session_only_path_third_party;
base::FilePath session_only_subdomain_path_third_party;
base::FilePath inverted_normal_path_third_party;
base::FilePath inverted_session_only_path_third_party;
base::FilePath inverted_session_only_subdomain_path_third_party;
normal_path_first_party =
GetFilePathForTesting(kNormalFirstPartyBucketLocator);
session_only_path_first_party =
GetFilePathForTesting(kSessionOnlyFirstPartyBucketLocator);
session_only_subdomain_path_first_party =
GetFilePathForTesting(kSessionOnlySubdomainFirstPartyBucketLocator);
normal_path_third_party =
GetFilePathForTesting(kNormalThirdPartyBucketLocator);
session_only_path_third_party =
GetFilePathForTesting(kSessionOnlyThirdPartyBucketLocator);
session_only_subdomain_path_third_party =
GetFilePathForTesting(kSessionOnlySubdomainThirdPartyBucketLocator);
inverted_normal_path_third_party =
GetFilePathForTesting(kInvertedNormalThirdPartyBucketLocator);
inverted_session_only_path_third_party =
GetFilePathForTesting(kInvertedSessionOnlyThirdPartyBucketLocator);
inverted_session_only_subdomain_path_third_party = GetFilePathForTesting(
kInvertedSessionOnlySubdomainThirdPartyBucketLocator);
if (IsThirdPartyStoragePartitioningEnabled()) {
EXPECT_NE(normal_path_first_party, normal_path_third_party);
EXPECT_NE(session_only_path_first_party, session_only_path_third_party);
EXPECT_NE(session_only_subdomain_path_first_party,
session_only_subdomain_path_third_party);
EXPECT_NE(inverted_normal_path_third_party,
inverted_session_only_path_third_party);
EXPECT_NE(inverted_normal_path_third_party,
inverted_session_only_subdomain_path_third_party);
} else {
EXPECT_EQ(normal_path_first_party, normal_path_third_party);
EXPECT_EQ(session_only_path_first_party, session_only_path_third_party);
EXPECT_EQ(session_only_subdomain_path_first_party,
session_only_subdomain_path_third_party);
EXPECT_EQ(inverted_normal_path_third_party,
inverted_session_only_path_third_party);
EXPECT_EQ(inverted_normal_path_third_party,
inverted_session_only_subdomain_path_third_party);
}
ASSERT_TRUE(base::CreateDirectory(normal_path_first_party));
ASSERT_TRUE(base::CreateDirectory(session_only_path_first_party));
ASSERT_TRUE(base::CreateDirectory(session_only_subdomain_path_first_party));
ASSERT_TRUE(base::CreateDirectory(normal_path_third_party));
ASSERT_TRUE(base::CreateDirectory(session_only_path_third_party));
ASSERT_TRUE(base::CreateDirectory(session_only_subdomain_path_third_party));
ASSERT_TRUE(base::CreateDirectory(inverted_normal_path_third_party));
ASSERT_TRUE(base::CreateDirectory(inverted_session_only_path_third_party));
ASSERT_TRUE(
base::CreateDirectory(inverted_session_only_subdomain_path_third_party));
base::RunLoop run_loop;
context()->ForceInitializeFromFilesForTesting(run_loop.QuitClosure());
run_loop.Run();
IndexedDBContextImpl::Shutdown(std::move(context_));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(base::DirectoryExists(normal_path_first_party));
EXPECT_TRUE(base::DirectoryExists(session_only_path_first_party));
EXPECT_FALSE(base::DirectoryExists(session_only_subdomain_path_first_party));
EXPECT_TRUE(base::DirectoryExists(normal_path_third_party));
EXPECT_TRUE(base::DirectoryExists(session_only_path_third_party));
EXPECT_FALSE(base::DirectoryExists(session_only_subdomain_path_third_party));
EXPECT_TRUE(base::DirectoryExists(inverted_normal_path_third_party));
if (IsThirdPartyStoragePartitioningEnabled()) {
EXPECT_FALSE(base::DirectoryExists(inverted_session_only_path_third_party));
EXPECT_FALSE(base::DirectoryExists(
inverted_session_only_subdomain_path_third_party));
} else {
EXPECT_TRUE(base::DirectoryExists(inverted_session_only_path_third_party));
EXPECT_TRUE(base::DirectoryExists(
inverted_session_only_subdomain_path_third_party));
}
}
TEST_P(IndexedDBTest, SetForceKeepSessionState) {
base::FilePath normal_path_first_party;
base::FilePath session_only_path_first_party;
base::FilePath normal_path_third_party;
base::FilePath session_only_path_third_party;
context()->SetForceKeepSessionState();
normal_path_first_party =
GetFilePathForTesting(kNormalFirstPartyBucketLocator);
session_only_path_first_party =
GetFilePathForTesting(kSessionOnlyFirstPartyBucketLocator);
normal_path_third_party =
GetFilePathForTesting(kNormalThirdPartyBucketLocator);
session_only_path_third_party =
GetFilePathForTesting(kSessionOnlyThirdPartyBucketLocator);
if (IsThirdPartyStoragePartitioningEnabled()) {
EXPECT_NE(normal_path_first_party, normal_path_third_party);
EXPECT_NE(session_only_path_first_party, session_only_path_third_party);
} else {
EXPECT_EQ(normal_path_first_party, normal_path_third_party);
EXPECT_EQ(session_only_path_first_party, session_only_path_third_party);
}
ASSERT_TRUE(base::CreateDirectory(normal_path_first_party));
ASSERT_TRUE(base::CreateDirectory(session_only_path_first_party));
ASSERT_TRUE(base::CreateDirectory(normal_path_third_party));
ASSERT_TRUE(base::CreateDirectory(session_only_path_third_party));
context()->ForceInitializeFromFilesForTesting(base::DoNothing());
base::RunLoop().RunUntilIdle();
IndexedDBContextImpl::Shutdown(std::move(context_));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(base::DirectoryExists(normal_path_first_party));
EXPECT_TRUE(base::DirectoryExists(session_only_path_first_party));
EXPECT_TRUE(base::DirectoryExists(normal_path_third_party));
EXPECT_TRUE(base::DirectoryExists(session_only_path_third_party));
}
TEST_P(IndexedDBTest, Bug464999826) {
quota_manager_->HoldBackResults();
base::FilePath db_directory =
GetFilePathForTesting(kNormalFirstPartyBucketLocator);
ASSERT_TRUE(base::CreateDirectory(db_directory));
context()->ForceInitializeFromFilesForTesting(base::DoNothing());
scoped_refptr<base::SequencedTaskRunner> idb_task_runner =
context_->idb_task_runner();
IndexedDBContextImpl::Shutdown(std::move(context_));
base::RunLoop destruction_loop;
idb_task_runner->PostTask(FROM_HERE, destruction_loop.QuitClosure());
destruction_loop.Run();
quota_manager_->ReleaseResults();
}
TEST_P(IndexedDBTest, ForceCloseOpenDatabasesOnDelete) {
storage::BucketInfo bucket_info;
VerifyForcedClosedCalled(
base::BindOnce(base::IgnoreResult(&IndexedDBTest::DeleteBucket),
base::Unretained(this), &bucket_info),
&bucket_info);
base::FilePath test_path =
GetFilePathForTesting(bucket_info.ToBucketLocator());
EXPECT_FALSE(base::DirectoryExists(test_path));
}
TEST_P(IndexedDBTest, ForceCloseOpenDatabasesOnDatabaseError) {
storage::BucketInfo bucket_info;
VerifyForcedClosedCalled(
base::BindOnce(
[](IndexedDBTest* test, storage::BucketInfo* bucket_info) {
BucketContext* bucket = test->GetBucketContext(bucket_info->id);
const auto& dbs = bucket->GetDatabasesForTesting();
ASSERT_EQ(1U, dbs.size());
for (auto& [name, db] : dbs) {
bucket->OnDatabaseError(
db.get(), Status::InvalidArgument("operation not supported"),
std::string());
}
},
this, &bucket_info),
&bucket_info);
}
TEST_P(IndexedDBTest, ForceCloseOpenDatabasesOnDeleteDatabase) {
storage::BucketInfo bucket_info;
VerifyForcedClosedCalled(
base::BindOnce(
[](mojo::Remote<blink::mojom::IDBFactory>* factory_remote) {
MockMojoFactoryClient delete_client;
(*factory_remote)
->DeleteDatabase(delete_client.CreateInterfacePtrAndBind(),
u"opendb",
true);
},
&this->factory_remote_),
&bucket_info);
base::FilePath test_path =
GetFilePathForTesting(bucket_info.ToBucketLocator());
EXPECT_TRUE(base::DirectoryExists(test_path));
}
TEST_P(IndexedDBTest, AvoidCrashAfterForceCloseDbAndThenOpen) {
storage::BucketInfo bucket_info = InitBucket(GetTestStorageKey());
BucketLocator bucket_locator = bucket_info.ToBucketLocator();
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote_.BindNewPipeAndPassReceiver(), bucket_info);
base::RunLoop run_loop_for_first_open;
MockMojoDatabaseCallbacks database_callbacks;
EXPECT_CALL(database_callbacks, ForcedClose())
.WillOnce(
::base::test::RunClosure(run_loop_for_first_open.QuitClosure()));
MockMojoFactoryClient client;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
EXPECT_CALL(client, MockedOpenSuccess)
.WillOnce(testing::DoAll(MoveArgPointee<0>(&pending_database)));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote_->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(),
u"opendb", 0,
transaction_remote.BindNewEndpointAndPassReceiver(),
0, 0);
MockMojoFactoryClient delete_client;
factory_remote_->DeleteDatabase(delete_client.CreateInterfacePtrAndBind(),
u"opendb",
true);
MockMojoFactoryClient client2;
EXPECT_CALL(client2, Error);
MockMojoDatabaseCallbacks database_callbacks2;
base::RunLoop run_loop_for_second_open;
EXPECT_CALL(database_callbacks2, ForcedClose())
.WillOnce(
::base::test::RunClosure(run_loop_for_second_open.QuitClosure()));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote2;
factory_remote_->Open(
client2.CreateInterfacePtrAndBind(),
database_callbacks2.CreateInterfacePtrAndBind(), u"opendb",
0, transaction_remote2.BindNewEndpointAndPassReceiver(),
42, 0);
run_loop_for_first_open.Run();
run_loop_for_second_open.Run();
}
TEST_P(IndexedDBTest, BasicFactoryCreationAndTearDown) {
const blink::StorageKey storage_key_1 =
blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
storage::BucketInfo bucket_1 = GetOrCreateBucket(
storage::BucketInitParams::ForDefaultBucket(storage_key_1));
BucketLocator bucket_locator_1 = bucket_1.ToBucketLocator();
base::FilePath file_1 =
GetFilePathForTesting(bucket_locator_1).AppendASCII("1.json");
ASSERT_TRUE(CreateDirectory(file_1.DirName()));
ASSERT_TRUE(base::WriteFile(file_1, std::string(10, 'a')));
const blink::StorageKey storage_key_2 =
blink::StorageKey::CreateFromStringForTesting("http://localhost:82");
storage::BucketInfo bucket_2 = GetOrCreateBucket(
storage::BucketInitParams::ForDefaultBucket(storage_key_2));
BucketLocator bucket_locator_2 = bucket_2.ToBucketLocator();
base::FilePath file_2 =
GetFilePathForTesting(bucket_locator_2).AppendASCII("2.json");
ASSERT_TRUE(CreateDirectory(file_2.DirName()));
ASSERT_TRUE(base::WriteFile(file_2, std::string(100, 'a')));
const blink::StorageKey storage_key_3 =
blink::StorageKey::CreateFromStringForTesting("http://localhost2:82");
storage::BucketInfo bucket_3 = GetOrCreateBucket(
storage::BucketInitParams::ForDefaultBucket(storage_key_3));
BucketLocator bucket_locator_3 = bucket_3.ToBucketLocator();
base::FilePath file_3 =
GetFilePathForTesting(bucket_locator_3).AppendASCII("3.json");
ASSERT_TRUE(CreateDirectory(file_3.DirName()));
ASSERT_TRUE(base::WriteFile(file_3, std::string(1000, 'a')));
const blink::StorageKey storage_key_4 = blink::StorageKey::Create(
storage_key_1.origin(), net::SchemefulSite(storage_key_3.origin()),
blink::mojom::AncestorChainBit::kCrossSite);
storage::BucketInfo bucket_4 = GetOrCreateBucket(
storage::BucketInitParams::ForDefaultBucket(storage_key_4));
BucketLocator bucket_locator_4 = bucket_4.ToBucketLocator();
base::FilePath file_4 =
GetFilePathForTesting(bucket_locator_4).AppendASCII("4.json");
ASSERT_TRUE(CreateDirectory(file_4.DirName()));
ASSERT_TRUE(base::WriteFile(file_4, std::string(10000, 'a')));
const blink::StorageKey storage_key_5 = storage_key_1;
storage::BucketInitParams params(storage_key_5, "inbox");
storage::BucketInfo bucket_5 = GetOrCreateBucket(params);
BucketLocator bucket_locator_5 = bucket_5.ToBucketLocator();
base::FilePath file_5 =
GetFilePathForTesting(bucket_locator_5).AppendASCII("5.json");
ASSERT_TRUE(CreateDirectory(file_5.DirName()));
ASSERT_TRUE(base::WriteFile(file_5, std::string(20000, 'a')));
EXPECT_NE(file_5.DirName(), file_1.DirName());
GetOrCreateBucketContext(bucket_1, context()->GetDataPath(bucket_locator_1))
.InitBackingStore(true);
GetOrCreateBucketContext(bucket_2, context()->GetDataPath(bucket_locator_2))
.InitBackingStore(true);
GetOrCreateBucketContext(bucket_3, context()->GetDataPath(bucket_locator_3))
.InitBackingStore(true);
GetOrCreateBucketContext(bucket_4, context()->GetDataPath(bucket_locator_4))
.InitBackingStore(true);
GetOrCreateBucketContext(bucket_5, context()->GetDataPath(bucket_locator_5))
.InitBackingStore(true);
int64_t bucket_size_1 = base::ComputeDirectorySize(file_1.DirName());
int64_t bucket_size_4 = base::ComputeDirectorySize(file_4.DirName());
int64_t bucket_size_5 = base::ComputeDirectorySize(file_5.DirName());
if (IsThirdPartyStoragePartitioningEnabled()) {
EXPECT_NE(bucket_size_1, bucket_size_4);
}
EXPECT_NE(bucket_size_1, bucket_size_5);
if (IsThirdPartyStoragePartitioningEnabled()) {
EXPECT_EQ(5ul, context_->GetOpenBucketIdsForTesting().size());
} else {
EXPECT_EQ(4ul, context_->GetOpenBucketIdsForTesting().size());
}
}
TEST_P(IndexedDBTest, CloseSequenceStarts) {
BucketContextHandle bucket_context_handle = CreateBucketHandle();
const storage::BucketId bucket_id =
bucket_context_handle->bucket_locator().id;
bucket_context_handle.Release();
VerifyBucketContext(bucket_id, true,
true);
EXPECT_TRUE(GetBucketContext(bucket_id)->IsClosing());
context_->ForceClose(bucket_id, {}, base::DoNothing());
VerifyBucketContextWaitIfNeeded(bucket_id, false);
}
TEST_P(IndexedDBTest, CloseWithReceiversActive) {
BucketContextHandle bucket_context_handle = CreateBucketHandle();
const storage::BucketId bucket_id =
bucket_context_handle->bucket_locator().id;
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
bucket_context_handle->AddReceiver(
storage::BucketClientInfo{}, std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver());
VerifyBucketContext(bucket_id, true,
true);
bucket_context_handle.Release();
task_environment_.FastForwardBy(base::Seconds(2));
VerifyBucketContext(bucket_id, true,
false);
factory_remote.reset();
task_environment_.RunUntilIdle();
VerifyBucketContext(bucket_id, false);
}
TEST_P(IndexedDBTest, CloseWithReceiversInactive) {
BucketContextHandle bucket_context_handle = CreateBucketHandle();
const storage::BucketId bucket_id =
bucket_context_handle->bucket_locator().id;
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
bucket_context_handle->AddReceiver(
storage::BucketClientInfo{}, std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver());
VerifyBucketContext(bucket_id, true,
true);
factory_remote.reset();
task_environment_.RunUntilIdle();
VerifyBucketContext(bucket_id, true,
true);
bucket_context_handle.Release();
task_environment_.FastForwardBy(base::Seconds(2));
VerifyBucketContext(bucket_id, false);
}
TEST_P(IndexedDBTest, PreCloseTasksStart) {
if (IsSqliteBackingStoreEnabled()) {
GTEST_SKIP();
}
{
BucketContextHandle bucket_context_handle = CreateBucketHandle();
storage::BucketId bucket_id = bucket_context_handle->bucket_locator().id;
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_context_handle->bucket_locator()));
bucket_context_handle.Release();
VerifyBucketContext(bucket_id, true,
true);
EXPECT_TRUE(GetBucketContext(bucket_id)->IsClosing());
EXPECT_EQ(BucketContext::ClosingState::kPreCloseGracePeriod,
GetBucketContext(bucket_id)->closing_stage());
task_environment_.FastForwardBy(base::Seconds(2));
VerifyBucketContext(bucket_id, true,
false);
}
task_environment_.FastForwardBy(kMaxGlobalSweepDelay);
BucketContext* bucket_context = nullptr;
{
BucketContextHandle bucket_context_handle = CreateBucketHandle();
bucket_context = bucket_context_handle.bucket_context();
bucket_context_handle.Release();
bucket_context->close_timer()->FireNow();
EXPECT_EQ(BucketContext::ClosingState::kRunningPreCloseTasks,
bucket_context->closing_stage());
}
{
BucketContextHandle bucket_context_handle(*bucket_context);
storage::BucketId bucket_id = bucket_context_handle->bucket_locator().id;
EXPECT_NE(BucketContext::ClosingState::kRunningPreCloseTasks,
bucket_context->closing_stage());
task_environment_.FastForwardBy(kMaxGlobalSweepDelay);
bucket_context_handle.Release();
EXPECT_EQ(BucketContext::ClosingState::kPreCloseGracePeriod,
bucket_context->closing_stage());
bucket_context->close_timer()->FireNow();
ASSERT_TRUE(context_->BucketContextExists(bucket_id));
EXPECT_TRUE(!!bucket_context->backing_store());
VerifyBucketContextWaitIfNeeded(bucket_id,
false);
}
{
task_environment_.FastForwardBy(kMaxBucketSweepDelay);
BucketContextHandle bucket_context_handle = CreateBucketHandle();
bucket_context = bucket_context_handle.bucket_context();
storage::BucketId bucket_id = bucket_context_handle->bucket_locator().id;
bucket_context_handle.Release();
bucket_context->close_timer()->FireNow();
ASSERT_TRUE(context_->BucketContextExists(bucket_id));
EXPECT_EQ(BucketContext::ClosingState::kRunningPreCloseTasks,
bucket_context->closing_stage());
}
}
TEST_P(IndexedDBTest, InMemoryFactoriesStay) {
SetUpInMemoryContext();
BucketContextHandle bucket_context_handle = CreateBucketHandle();
BucketLocator bucket_locator = bucket_context_handle->bucket_locator();
EXPECT_TRUE(bucket_context_handle->in_memory());
BucketContext* bucket_context = bucket_context_handle.bucket_context();
bucket_context_handle.Release();
RunPostedTasks();
EXPECT_TRUE(context_->BucketContextExists(bucket_locator.id));
EXPECT_FALSE(bucket_context->IsClosing());
context_->ForceClose(
bucket_locator.id,
storage::mojom::ForceCloseReason::FORCE_CLOSE_INTERNALS_PAGE,
base::DoNothing());
RunPostedTasks();
RunPostedTasks();
RunPostedTasks();
VerifyBucketContext(bucket_locator.id, true,
true);
context_->ForceClose(
bucket_locator.id,
storage::mojom::ForceCloseReason::FORCE_CLOSE_DELETE_ORIGIN,
base::DoNothing());
VerifyBucketContextWaitIfNeeded(bucket_locator.id,
false);
}
TEST_P(IndexedDBTest, TooLongOrigin) {
base::FilePath temp_dir =
context()->GetFirstPartyDataPathForTesting().DirName();
int limit = base::GetMaximumPathComponentLength(temp_dir);
EXPECT_GT(limit, 0);
std::string origin(limit + 1, 'x');
const blink::StorageKey too_long_storage_key =
blink::StorageKey::CreateFromStringForTesting("http://" + origin +
":81/");
storage::BucketInfo bucket_info = GetOrCreateBucket(
storage::BucketInitParams::ForDefaultBucket(too_long_storage_key));
BucketLocator bucket_locator = bucket_info.ToBucketLocator();
BucketContextHandle bucket_context_handle(GetOrCreateBucketContext(
ToBucketInfo(bucket_locator), context()->GetDataPath(bucket_locator)));
Status s;
std::tie(s, std::ignore, std::ignore) =
bucket_context_handle->InitBackingStore(
true);
EXPECT_TRUE(s.IsIOError());
}
TEST_P(IndexedDBTest, FactoryForceClose) {
BucketContextHandle bucket_context_handle = CreateBucketHandle();
BucketLocator bucket_locator = bucket_context_handle->bucket_locator();
bucket_context_handle->ForceClose(
false, "The database is force-closed for testing.");
BucketContext* bucket_context = bucket_context_handle.bucket_context();
bucket_context_handle.Release();
ASSERT_TRUE(context_->BucketContextExists(bucket_locator.id));
EXPECT_TRUE(!!bucket_context->backing_store());
VerifyBucketContextWaitIfNeeded(bucket_locator.id,
false);
}
TEST_P(IndexedDBTest, CloseThenAddReceiver) {
const blink::StorageKey storage_key =
blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
BucketLocator bucket_locator = BucketLocator();
bucket_locator.storage_key = storage_key;
mojo::Remote<blink::mojom::IDBFactory> factory_remote1;
BindFactory(
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>(),
factory_remote1.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
ASSERT_TRUE(context()->BucketContextExists(bucket_locator.id));
factory_remote1.reset();
ASSERT_TRUE(context()->BucketContextExists(bucket_locator.id));
mojo::Remote<blink::mojom::IDBFactory> factory_remote2;
BindFactory(
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>(),
factory_remote2.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
factory_remote2.FlushForTesting();
}
TEST_P(IndexedDBTest, ConnectionCloseDuringUpgrade) {
const blink::StorageKey storage_key =
blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
BucketLocator bucket_locator = BucketLocator();
bucket_locator.storage_key = storage_key;
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
base::RunLoop run_loop;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
EXPECT_CALL(client, MockedUpgradeNeeded)
.WillOnce(
testing::DoAll(MoveArgPointee<0>(&pending_database),
::base::test::RunClosure(run_loop.QuitClosure())));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(), u"db",
1,
transaction_remote.BindNewEndpointAndPassReceiver(),
1, 0);
run_loop.Run();
ASSERT_TRUE(context_->BucketContextExists(bucket_locator.id));
EXPECT_FALSE(GetBucketContext(bucket_locator.id)->IsClosing());
pending_database.reset();
factory_remote.FlushForTesting();
EXPECT_TRUE(GetBucketContext(bucket_locator.id)->IsClosing());
}
TEST_P(IndexedDBTest, DeleteDatabase) {
const blink::StorageKey storage_key =
blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
BucketLocator bucket_locator = BucketLocator();
bucket_locator.storage_key = storage_key;
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
{
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
base::RunLoop run_loop;
EXPECT_CALL(client, DeleteSuccess(0))
.WillOnce(::base::test::RunClosure(run_loop.QuitClosure()));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->DeleteDatabase(client.CreateInterfacePtrAndBind(), u"db",
false);
run_loop.Run();
ASSERT_TRUE(context_->BucketContextExists(bucket_locator.id));
EXPECT_FALSE(GetBucketContext(bucket_locator.id)->backing_store());
}
{
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
base::RunLoop run_loop;
EXPECT_CALL(client, MockedUpgradeNeeded)
.WillOnce(::base::test::RunClosure(run_loop.QuitClosure()));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(), u"db",
blink::IndexedDBDatabaseMetadata::NO_VERSION,
transaction_remote.BindNewEndpointAndPassReceiver(),
1, 0);
run_loop.Run();
}
{
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
base::RunLoop run_loop;
EXPECT_CALL(client, DeleteSuccess(0))
.WillOnce(::base::test::RunClosure(run_loop.QuitClosure()));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->DeleteDatabase(client.CreateInterfacePtrAndBind(), u"db",
false);
run_loop.Run();
ASSERT_TRUE(context_->BucketContextExists(bucket_locator.id));
EXPECT_TRUE(GetBucketContext(bucket_locator.id)->IsClosing());
}
}
TEST_P(IndexedDBTest, DeleteDatabase_Cold) {
const blink::StorageKey storage_key =
blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
BucketLocator bucket_locator = BucketLocator();
bucket_locator.storage_key = storage_key;
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
{
base::HistogramTester histogram_tester;
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
base::RunLoop upgrade_run_loop;
EXPECT_CALL(client, MockedUpgradeNeeded)
.WillOnce(testing::DoAll(
MoveArgPointee<0>(&pending_database),
::base::test::RunClosure(upgrade_run_loop.QuitClosure())));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(), u"db",
1,
transaction_remote.BindNewEndpointAndPassReceiver(),
1, 0);
upgrade_run_loop.Run();
mojo::AssociatedRemote<blink::mojom::IDBDatabase> connection(
std::move(pending_database));
transaction_remote->Commit(0);
base::RunLoop success_run_loop;
EXPECT_CALL(client, MockedOpenSuccess)
.WillOnce(::base::test::RunClosure(success_run_loop.QuitClosure()));
success_run_loop.Run();
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.CreateIfMissing.OnDisk",
0 , 1);
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.CreateOrOpenDatabase.OnDisk",
0 , 1);
}
task_environment_.FastForwardBy(base::Seconds(2));
VerifyBucketContext(bucket_locator.id, true,
false);
{
base::HistogramTester histogram_tester;
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
base::RunLoop run_loop;
EXPECT_CALL(client, DeleteSuccess(1))
.WillOnce(::base::test::RunClosure(run_loop.QuitClosure()));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->DeleteDatabase(client.CreateInterfacePtrAndBind(), u"db",
false);
run_loop.Run();
histogram_tester.ExpectTotalCount(
"IndexedDB.BackingStore.CreateIfMissing.OnDisk", 0);
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.CreateOrOpenDatabase.OnDisk",
0 , 1);
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.DeleteDatabase.OnDisk", 0 ,
1);
}
}
TEST_P(IndexedDBTest, DeleteDatabase_DuplicateRequests) {
const blink::StorageKey storage_key =
blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
BucketLocator bucket_locator = BucketLocator();
bucket_locator.storage_key = storage_key;
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
mojo::AssociatedRemote<blink::mojom::IDBDatabase> connection;
{
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
base::RunLoop upgrade_run_loop;
EXPECT_CALL(client, MockedUpgradeNeeded)
.WillOnce(testing::DoAll(
MoveArgPointee<0>(&pending_database),
::base::test::RunClosure(upgrade_run_loop.QuitClosure())));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->Open(
client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(), kDatabaseName,
1, transaction_remote.BindNewEndpointAndPassReceiver(),
1, 0);
upgrade_run_loop.Run();
connection.Bind(std::move(pending_database));
transaction_remote->Commit(0);
EXPECT_CALL(database_callbacks, Complete);
base::RunLoop success_run_loop;
EXPECT_CALL(client, MockedOpenSuccess)
.WillOnce(::base::test::RunClosure(success_run_loop.QuitClosure()));
success_run_loop.Run();
}
base::HistogramTester histogram_tester;
base::RunLoop run_loop;
MockMojoFactoryClient first_client;
EXPECT_CALL(first_client, DeleteSuccess(1));
MockMojoFactoryClient second_client;
EXPECT_CALL(second_client, DeleteSuccess(0))
.WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
factory_remote->DeleteDatabase(first_client.CreateInterfacePtrAndBind(),
kDatabaseName,
false);
factory_remote->DeleteDatabase(second_client.CreateInterfacePtrAndBind(),
kDatabaseName,
false);
connection.reset();
run_loop.Run();
histogram_tester.ExpectTotalCount(
"IndexedDB.BackingStore.CreateOrOpenDatabase.OnDisk", 0);
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.DeleteDatabase.OnDisk", 0 ,
1);
}
TEST_P(IndexedDBTest, GetDatabaseNames_NoFactory) {
base::HistogramTester histogram_tester;
const blink::StorageKey storage_key =
blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
BucketLocator bucket_locator = BucketLocator();
bucket_locator.storage_key = storage_key;
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
{
base::test::TestFuture<std::vector<blink::mojom::IDBNameAndVersionPtr>,
blink::mojom::IDBErrorPtr>
info_future;
factory_remote->GetDatabaseInfo(info_future.GetCallback());
ASSERT_TRUE(info_future.Wait());
EXPECT_FALSE(GetBucketContext(bucket_locator.id)->backing_store());
histogram_tester.ExpectTotalCount(
"IndexedDB.IDBFactory.GetDatabaseInfo.Duration.OnDisk", 0);
}
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
base::RunLoop run_loop;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
EXPECT_CALL(client, MockedUpgradeNeeded)
.WillOnce(
testing::DoAll(MoveArgPointee<0>(&pending_database),
::base::test::RunClosure(run_loop.QuitClosure())));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(), u"db",
blink::IndexedDBDatabaseMetadata::NO_VERSION,
transaction_remote.BindNewEndpointAndPassReceiver(),
1, 0);
run_loop.Run();
{
base::test::TestFuture<std::vector<blink::mojom::IDBNameAndVersionPtr>,
blink::mojom::IDBErrorPtr>
info_future;
factory_remote->GetDatabaseInfo(info_future.GetCallback());
ASSERT_TRUE(info_future.Wait());
ASSERT_TRUE(context_->BucketContextExists(bucket_locator.id));
EXPECT_FALSE(GetBucketContext(bucket_locator.id)->IsClosing());
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.GetDatabaseNamesAndVersions.OnDisk",
0 , 1);
histogram_tester.ExpectTotalCount(
"IndexedDB.IDBFactory.GetDatabaseInfo.Duration.OnDisk", 1);
}
}
TEST_P(IndexedDBTest, UpdatePriorityAfterForceClose) {
const blink::StorageKey storage_key =
blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
BucketLocator bucket_locator = BucketLocator();
bucket_locator.storage_key = storage_key;
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
base::RunLoop run_loop;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
EXPECT_CALL(client, MockedUpgradeNeeded)
.WillOnce(
testing::DoAll(MoveArgPointee<0>(&pending_database),
::base::test::RunClosure(run_loop.QuitClosure())));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(), u"db",
blink::IndexedDBDatabaseMetadata::NO_VERSION,
transaction_remote.BindNewEndpointAndPassReceiver(),
1, 0);
run_loop.Run();
mojo::AssociatedRemote<blink::mojom::IDBDatabase> connection(
std::move(pending_database));
context_->ForceClose(bucket_locator.id, {}, base::DoNothing());
connection->UpdatePriority(1);
connection.FlushForTesting();
}
TEST_P(IndexedDBTest, TransactionHistograms) {
constexpr int64_t kObjectStoreId = 1;
int64_t transaction_id = 0;
const blink::StorageKey storage_key =
blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
BucketLocator bucket_locator = BucketLocator();
bucket_locator.storage_key = storage_key;
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
mojo::AssociatedRemote<blink::mojom::IDBDatabase> connection;
{
base::HistogramTester histogram_tester;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
base::RunLoop upgrade_run_loop;
EXPECT_CALL(client, MockedUpgradeNeeded)
.WillOnce(testing::DoAll(
MoveArgPointee<0>(&pending_database),
::base::test::RunClosure(upgrade_run_loop.QuitClosure())));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction;
factory_remote->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(), u"db",
1,
transaction.BindNewEndpointAndPassReceiver(),
++transaction_id, 0);
upgrade_run_loop.Run();
connection.Bind(std::move(pending_database));
transaction->CreateObjectStore(kObjectStoreId, u"store",
blink::IndexedDBKeyPath(),
true);
transaction->Commit(0);
base::RunLoop loop;
EXPECT_CALL(database_callbacks, Complete(transaction_id))
.WillOnce(base::test::RunClosure(loop.QuitClosure()));
loop.Run();
EXPECT_CALL(client, MockedOpenSuccess);
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.BeginTransaction.OnDisk",
0 , 1);
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.ChangeDatabaseVersion.OnDisk",
0 , 1);
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.CreateObjectStore.OnDisk",
0 , 1);
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.CommitPhaseOne.OnDisk", 0 ,
1);
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.CommitPhaseTwo.OnDisk", 0 ,
1);
}
{
base::HistogramTester histogram_tester;
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction;
connection->CreateTransaction(
transaction.BindNewEndpointAndPassReceiver(), ++transaction_id,
{kObjectStoreId}, blink::mojom::IDBTransactionMode::ReadWrite,
blink::mojom::IDBTransactionDurability::Relaxed);
transaction->Commit(0);
base::RunLoop loop;
EXPECT_CALL(database_callbacks, Complete(transaction_id))
.WillOnce(base::test::RunClosure(loop.QuitClosure()));
loop.Run();
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.BeginTransaction.OnDisk",
0 , 1);
histogram_tester.ExpectTotalCount(
"IndexedDB.BackingStore.CommitPhaseOne.OnDisk", 0);
histogram_tester.ExpectTotalCount(
"IndexedDB.BackingStore.CommitPhaseTwo.OnDisk", 0);
}
{
base::HistogramTester histogram_tester;
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction;
connection->CreateTransaction(
transaction.BindNewEndpointAndPassReceiver(), ++transaction_id,
{kObjectStoreId}, blink::mojom::IDBTransactionMode::ReadWrite,
blink::mojom::IDBTransactionDurability::Relaxed);
transaction->Put(kObjectStoreId,
blink::mojom::IDBValuePtr(blink::mojom::IDBValue::New()),
blink::IndexedDBKey(), blink::mojom::IDBPutMode::AddOnly,
{},
base::BindLambdaForTesting(
[&](blink::mojom::IDBTransactionPutResultPtr result) {
EXPECT_FALSE(result->is_error_result());
}));
transaction->Commit(0);
base::RunLoop loop;
EXPECT_CALL(database_callbacks, Complete(transaction_id))
.WillOnce(base::test::RunClosure(loop.QuitClosure()));
loop.Run();
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.BeginTransaction.OnDisk",
0 , 1);
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.PutRecord.OnDisk", 0 , 1);
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.CommitPhaseOne.OnDisk", 0 ,
1);
histogram_tester.ExpectTotalCount(
"IndexedDB.BackingStore.WriteBlobs.OnDisk", 0);
histogram_tester.ExpectUniqueSample(
"IndexedDB.BackingStore.CommitPhaseTwo.OnDisk", 0 ,
1);
}
}
TEST_P(IndexedDBTest, QuotaErrorOnDbOpenError) {
base::HistogramTester histograms;
if (IsSqliteBackingStoreEnabled()) {
#if BUILDFLAG(IS_FUCHSIA)
GTEST_SKIP();
#endif
} else {
leveldb_env::SetDBFactoryForTesting(base::BindRepeating(
[](const leveldb_env::Options& options, const std::string& name,
std::unique_ptr<leveldb::DB>* dbptr) {
return leveldb_env::MakeIOError("foobar", "disk full",
leveldb_env::MethodID::kCreateDir,
base::File::FILE_ERROR_NO_SPACE);
}));
}
const blink::StorageKey storage_key =
blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
BucketLocator bucket_locator = BucketLocator();
bucket_locator.is_default = true;
bucket_locator.storage_key = storage_key;
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
std::optional<base::FilePermissionRestorer> permission_restorer;
if (IsSqliteBackingStoreEnabled()) {
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
base::RunLoop run_loop;
EXPECT_CALL(client, MockedUpgradeNeeded)
.WillOnce(::base::test::RunClosure(run_loop.QuitClosure()));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(), u"db2",
blink::IndexedDBDatabaseMetadata::NO_VERSION,
transaction_remote.BindNewEndpointAndPassReceiver(),
1, 0);
run_loop.Run();
base::FilePath data_path = GetFilePathForTesting(bucket_locator);
permission_restorer.emplace(data_path);
ASSERT_TRUE(base::MakeFileUnwritable(data_path))
<< base::File::GetLastFileError();
histograms.ExpectTotalCount("IndexedDB.SQLite.OpenRetryResult", 0);
}
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
base::RunLoop run_loop;
EXPECT_CALL(client, Error)
.WillOnce(::base::test::RunClosure(run_loop.QuitClosure()));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(), u"db",
1,
transaction_remote.BindNewEndpointAndPassReceiver(),
2, 0);
run_loop.Run();
if (IsSqliteBackingStoreEnabled()) {
histograms.ExpectUniqueSample("IndexedDB.SQLite.OpenRetryResult",
5 , 1);
}
ASSERT_EQ(1U, quota_manager_->write_error_tracker().size());
EXPECT_EQ(storage_key, quota_manager_->write_error_tracker().begin()->first);
EXPECT_EQ(1, quota_manager_->write_error_tracker().begin()->second);
leveldb_env::SetDBFactoryForTesting({});
}
TEST_P(IndexedDBTest, DatabaseFailedOpen) {
const blink::StorageKey storage_key =
blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
BucketLocator bucket_locator = BucketLocator();
bucket_locator.storage_key = storage_key;
const std::u16string db_name(u"db");
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
{
const int64_t db_version = 2;
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
base::RunLoop run_loop;
EXPECT_CALL(client, MockedUpgradeNeeded)
.WillOnce(::base::test::RunClosure(run_loop.QuitClosure()));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(),
db_name, db_version,
transaction_remote.BindNewEndpointAndPassReceiver(),
1, 0);
run_loop.Run();
}
{
const int64_t db_version = 1;
base::RunLoop run_loop;
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
EXPECT_CALL(client, Error)
.WillOnce(::base::test::RunClosure(run_loop.QuitClosure()));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(),
db_name, db_version,
transaction_remote.BindNewEndpointAndPassReceiver(),
2, 0);
run_loop.Run();
BucketContext* bucket_context = GetBucketContext(bucket_locator.id);
ASSERT_TRUE(bucket_context);
EXPECT_FALSE(
base::Contains(bucket_context->GetDatabasesForTesting(), db_name));
}
}
TEST_P(IndexedDBTest, DataLoss) {
const blink::StorageKey storage_key =
blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
BucketLocator bucket_locator = BucketLocator();
bucket_locator.storage_key = storage_key;
const std::u16string db_name(u"test_db");
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
{
base::AutoReset<IndexedDBDataFormatVersion> override_version(
&IndexedDBDataFormatVersion::GetMutableCurrentForTesting(),
IndexedDBDataFormatVersion(3, 4));
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
base::RunLoop run_loop;
EXPECT_CALL(client, MockedUpgradeNeeded(
_, _, blink::mojom::IDBDataLoss::None, _, _))
.WillOnce(
testing::DoAll(MoveArgPointee<0>(&pending_database),
::base::test::RunClosure(run_loop.QuitClosure())));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(),
db_name, 1,
transaction_remote.BindNewEndpointAndPassReceiver(),
1, 0);
run_loop.Run();
mojo::AssociatedRemote<blink::mojom::IDBDatabase> connection(
std::move(pending_database));
transaction_remote->Commit(0);
transaction_remote.FlushForTesting();
base::RunLoop run_loop2;
context_->ForceClose(
bucket_locator.id,
storage::mojom::ForceCloseReason::FORCE_CLOSE_BACKING_STORE_FAILURE,
run_loop2.QuitClosure());
run_loop2.Run();
}
{
base::HistogramTester histograms;
base::AutoReset<IndexedDBDataFormatVersion> override_version(
&IndexedDBDataFormatVersion::GetMutableCurrentForTesting(),
IndexedDBDataFormatVersion(3, 3));
base::RunLoop run_loop;
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
EXPECT_CALL(client, MockedUpgradeNeeded(
_, _, blink::mojom::IDBDataLoss::Total, _, _))
.WillOnce(::base::test::RunClosure(run_loop.QuitClosure()));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(),
db_name, 1,
transaction_remote.BindNewEndpointAndPassReceiver(),
2, 0);
run_loop.Run();
if (IsSqliteBackingStoreEnabled()) {
histograms.ExpectUniqueSample("IndexedDB.SQLite.OpenRetryResult",
0 , 1);
}
}
}
#if BUILDFLAG(IS_WIN)
TEST_P(IndexedDBTest, FilePathLengthLogging) {
base::HistogramTester histograms;
const blink::StorageKey storage_key =
blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
BucketLocator bucket_locator = BucketLocator();
bucket_locator.storage_key = storage_key;
{
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
{
const int64_t db_version = 1;
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
base::RunLoop run_loop;
EXPECT_CALL(client, MockedUpgradeNeeded)
.WillOnce(::base::test::RunClosure(run_loop.QuitClosure()));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(),
u"db", db_version,
transaction_remote.BindNewEndpointAndPassReceiver(),
1, 0);
run_loop.Run();
}
}
if (IsSqliteBackingStoreEnabled()) {
histograms.ExpectTotalCount("IndexedDB.FilePathLengthOverflow.LevelDB", 0);
} else {
histograms.ExpectUniqueSample("IndexedDB.FilePathLengthOverflow.LevelDB", 0,
1);
histograms.ExpectUniqueSample("IndexedDB.FilePathLengthOverflow.SQLite", 0,
1);
}
bucket_locator.storage_key = blink::StorageKey::CreateFromStringForTesting(
std::string("https://") + std::string(230, 'a') + ".com:81");
bucket_locator.id = storage::BucketId::FromUnsafeValue(2);
{
mojo::Remote<blink::mojom::IDBFactory> factory_remote;
mojo::PendingRemote<storage::mojom::IndexedDBClientStateChecker>
checker_remote;
BindFactory(std::move(checker_remote),
factory_remote.BindNewPipeAndPassReceiver(),
ToBucketInfo(bucket_locator));
{
const int64_t db_version = 1;
MockMojoFactoryClient client;
MockMojoDatabaseCallbacks database_callbacks;
base::RunLoop run_loop;
EXPECT_CALL(client, Error)
.WillOnce(::base::test::RunClosure(run_loop.QuitClosure()));
mojo::AssociatedRemote<blink::mojom::IDBTransaction> transaction_remote;
factory_remote->Open(client.CreateInterfacePtrAndBind(),
database_callbacks.CreateInterfacePtrAndBind(),
u"db", db_version,
transaction_remote.BindNewEndpointAndPassReceiver(),
1, 0);
run_loop.Run();
}
}
if (IsSqliteBackingStoreEnabled()) {
histograms.ExpectTotalCount("IndexedDB.FilePathLengthOverflow.LevelDB", 0);
} else {
histograms.ExpectTotalCount("IndexedDB.FilePathLengthOverflow.LevelDB", 2);
histograms.ExpectTotalCount("IndexedDB.FilePathLengthOverflow.SQLite", 2);
EXPECT_LT(
histograms.GetTotalSum("IndexedDB.FilePathLengthOverflow.LevelDB"),
histograms.GetTotalSum("IndexedDB.FilePathLengthOverflow.SQLite"));
}
}
#endif
}