blob: ddf9854d2e81507071e9073f7ca697869421171c [file] [log] [blame]
// Copyright (c) 2012 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 <stdint.h>
#include <utility>
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread.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/indexed_db_factory_impl.h"
#include "content/browser/indexed_db/mock_indexed_db_callbacks.h"
#include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
#include "content/browser/quota/mock_quota_manager_proxy.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/mock_special_storage_policy.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "storage/browser/quota/quota_manager.h"
#include "storage/browser/quota/special_storage_policy.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/origin.h"
using url::Origin;
namespace content {
class IndexedDBTest : public testing::Test {
public:
const Origin kNormalOrigin;
const Origin kSessionOnlyOrigin;
IndexedDBTest()
: kNormalOrigin(GURL("http://normal/")),
kSessionOnlyOrigin(GURL("http://session-only/")),
task_runner_(new base::TestSimpleTaskRunner),
special_storage_policy_(new MockSpecialStoragePolicy),
quota_manager_proxy_(new MockQuotaManagerProxy(nullptr, nullptr)) {
special_storage_policy_->AddSessionOnly(kSessionOnlyOrigin.GetURL());
}
~IndexedDBTest() override {
quota_manager_proxy_->SimulateQuotaManagerDestroyed();
}
protected:
void FlushIndexedDBTaskRunner() { task_runner_->RunUntilIdle(); }
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
scoped_refptr<MockSpecialStoragePolicy> special_storage_policy_;
scoped_refptr<MockQuotaManagerProxy> quota_manager_proxy_;
private:
TestBrowserThreadBundle thread_bundle_;
DISALLOW_COPY_AND_ASSIGN(IndexedDBTest);
};
TEST_F(IndexedDBTest, ClearSessionOnlyDatabases) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath normal_path;
base::FilePath session_only_path;
// Create the scope which will ensure we run the destructor of the context
// which should trigger the clean up.
{
scoped_refptr<IndexedDBContextImpl> idb_context = new IndexedDBContextImpl(
temp_dir.GetPath(), special_storage_policy_.get(),
quota_manager_proxy_.get(), task_runner_.get());
normal_path = idb_context->GetFilePathForTesting(kNormalOrigin);
session_only_path = idb_context->GetFilePathForTesting(kSessionOnlyOrigin);
ASSERT_TRUE(base::CreateDirectory(normal_path));
ASSERT_TRUE(base::CreateDirectory(session_only_path));
FlushIndexedDBTaskRunner();
base::RunLoop().RunUntilIdle();
quota_manager_proxy_->SimulateQuotaManagerDestroyed();
}
FlushIndexedDBTaskRunner();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(base::DirectoryExists(normal_path));
EXPECT_FALSE(base::DirectoryExists(session_only_path));
}
TEST_F(IndexedDBTest, SetForceKeepSessionState) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath normal_path;
base::FilePath session_only_path;
// Create the scope which will ensure we run the destructor of the context.
{
// Create some indexedDB paths.
// With the levelDB backend, these are directories.
scoped_refptr<IndexedDBContextImpl> idb_context = new IndexedDBContextImpl(
temp_dir.GetPath(), special_storage_policy_.get(),
quota_manager_proxy_.get(), task_runner_.get());
// Save session state. This should bypass the destruction-time deletion.
idb_context->SetForceKeepSessionState();
normal_path = idb_context->GetFilePathForTesting(kNormalOrigin);
session_only_path = idb_context->GetFilePathForTesting(kSessionOnlyOrigin);
ASSERT_TRUE(base::CreateDirectory(normal_path));
ASSERT_TRUE(base::CreateDirectory(session_only_path));
base::RunLoop().RunUntilIdle();
}
// Make sure we wait until the destructor has run.
base::RunLoop().RunUntilIdle();
// No data was cleared because of SetForceKeepSessionState.
EXPECT_TRUE(base::DirectoryExists(normal_path));
EXPECT_TRUE(base::DirectoryExists(session_only_path));
}
class ForceCloseDBCallbacks : public IndexedDBCallbacks {
public:
ForceCloseDBCallbacks(scoped_refptr<IndexedDBContextImpl> idb_context,
const Origin& origin)
: IndexedDBCallbacks(nullptr, origin, nullptr),
idb_context_(idb_context),
origin_(origin) {}
void OnSuccess() override {}
void OnSuccess(const std::vector<base::string16>&) override {}
void OnSuccess(std::unique_ptr<IndexedDBConnection> connection,
const IndexedDBDatabaseMetadata& metadata) override {
connection_ = std::move(connection);
idb_context_->ConnectionOpened(origin_, connection_.get());
}
IndexedDBConnection* connection() { return connection_.get(); }
protected:
~ForceCloseDBCallbacks() override {}
private:
scoped_refptr<IndexedDBContextImpl> idb_context_;
Origin origin_;
std::unique_ptr<IndexedDBConnection> connection_;
DISALLOW_COPY_AND_ASSIGN(ForceCloseDBCallbacks);
};
TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnDelete) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
scoped_refptr<MockIndexedDBDatabaseCallbacks> open_db_callbacks(
new MockIndexedDBDatabaseCallbacks());
scoped_refptr<MockIndexedDBDatabaseCallbacks> closed_db_callbacks(
new MockIndexedDBDatabaseCallbacks());
base::FilePath test_path;
// Create the scope which will ensure we run the destructor of the context.
{
TestBrowserContext browser_context;
const Origin kTestOrigin(GURL("http://test/"));
scoped_refptr<IndexedDBContextImpl> idb_context = new IndexedDBContextImpl(
temp_dir.GetPath(), special_storage_policy_.get(),
quota_manager_proxy_.get(), task_runner_.get());
scoped_refptr<ForceCloseDBCallbacks> open_callbacks =
new ForceCloseDBCallbacks(idb_context, kTestOrigin);
scoped_refptr<ForceCloseDBCallbacks> closed_callbacks =
new ForceCloseDBCallbacks(idb_context, kTestOrigin);
IndexedDBFactory* factory = idb_context->GetIDBFactory();
test_path = idb_context->GetFilePathForTesting(kTestOrigin);
std::unique_ptr<IndexedDBPendingConnection> open_connection(
base::MakeUnique<IndexedDBPendingConnection>(
open_callbacks, open_db_callbacks, 0 /* child_process_id */,
0 /* host_transaction_id */, 0 /* version */));
factory->Open(base::ASCIIToUTF16("opendb"), std::move(open_connection),
nullptr /* request_context */, Origin(kTestOrigin),
idb_context->data_path());
std::unique_ptr<IndexedDBPendingConnection> closed_connection(
base::MakeUnique<IndexedDBPendingConnection>(
closed_callbacks, closed_db_callbacks, 0 /* child_process_id */,
0 /* host_transaction_id */, 0 /* version */));
factory->Open(base::ASCIIToUTF16("closeddb"), std::move(closed_connection),
nullptr /* request_context */, Origin(kTestOrigin),
idb_context->data_path());
closed_callbacks->connection()->Close();
// TODO(jsbell): Remove static_cast<> when overloads are eliminated.
idb_context->TaskRunner()->PostTask(
FROM_HERE,
base::Bind(static_cast<void (IndexedDBContextImpl::*)(const Origin&)>(
&IndexedDBContextImpl::DeleteForOrigin),
idb_context, kTestOrigin));
FlushIndexedDBTaskRunner();
base::RunLoop().RunUntilIdle();
}
// Make sure we wait until the destructor has run.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(open_db_callbacks->forced_close_called());
EXPECT_FALSE(closed_db_callbacks->forced_close_called());
EXPECT_FALSE(base::DirectoryExists(test_path));
}
TEST_F(IndexedDBTest, DeleteFailsIfDirectoryLocked) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
const Origin kTestOrigin(GURL("http://test/"));
scoped_refptr<IndexedDBContextImpl> idb_context = new IndexedDBContextImpl(
temp_dir.GetPath(), special_storage_policy_.get(),
quota_manager_proxy_.get(), task_runner_.get());
base::FilePath test_path = idb_context->GetFilePathForTesting(kTestOrigin);
ASSERT_TRUE(base::CreateDirectory(test_path));
std::unique_ptr<LevelDBLock> lock =
LevelDBDatabase::LockForTesting(test_path);
ASSERT_TRUE(lock);
// TODO(jsbell): Remove static_cast<> when overloads are eliminated.
void (IndexedDBContextImpl::* delete_for_origin)(const Origin&) =
&IndexedDBContextImpl::DeleteForOrigin;
idb_context->TaskRunner()->PostTask(
FROM_HERE,
base::Bind(delete_for_origin, idb_context, kTestOrigin));
FlushIndexedDBTaskRunner();
EXPECT_TRUE(base::DirectoryExists(test_path));
}
TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnCommitFailure) {
const Origin kTestOrigin(GURL("http://test/"));
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
scoped_refptr<IndexedDBContextImpl> context = new IndexedDBContextImpl(
temp_dir.GetPath(), special_storage_policy_.get(),
quota_manager_proxy_.get(), task_runner_.get());
scoped_refptr<IndexedDBFactoryImpl> factory =
static_cast<IndexedDBFactoryImpl*>(context->GetIDBFactory());
scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks());
scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
new MockIndexedDBDatabaseCallbacks());
const int64_t transaction_id = 1;
std::unique_ptr<IndexedDBPendingConnection> connection(
base::MakeUnique<IndexedDBPendingConnection>(
callbacks, db_callbacks, 0 /* child_process_id */, transaction_id,
IndexedDBDatabaseMetadata::DEFAULT_VERSION));
factory->Open(base::ASCIIToUTF16("db"), std::move(connection),
nullptr /* request_context */, Origin(kTestOrigin),
temp_dir.GetPath());
EXPECT_TRUE(callbacks->connection());
// ConnectionOpened() is usually called by the dispatcher.
context->ConnectionOpened(kTestOrigin, callbacks->connection());
EXPECT_TRUE(factory->IsBackingStoreOpen(kTestOrigin));
// Simulate the write failure.
leveldb::Status status = leveldb::Status::IOError("Simulated failure");
context->GetIDBFactory()->HandleBackingStoreFailure(kTestOrigin);
EXPECT_TRUE(db_callbacks->forced_close_called());
EXPECT_FALSE(factory->IsBackingStoreOpen(kTestOrigin));
}
} // namespace content