blob: e43fe988e5b3c900034d0cfca2d70d2ee74f3556 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#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/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "components/services/storage/public/cpp/buckets/bucket_info.h"
#include "components/services/storage/public/cpp/buckets/constants.h"
#include "components/services/storage/public/cpp/quota_error_or.h"
#include "content/browser/native_io/native_io_manager.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/test_support/fake_message_dispatch_context.h"
#include "mojo/public/cpp/test_support/test_utils.h"
#include "storage/browser/quota/quota_manager_proxy.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 "storage/browser/test/quota_manager_proxy_sync.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/native_io/native_io.mojom.h"
#include "url/origin.h"
using ::blink::StorageKey;
using ::blink::mojom::NativeIOErrorPtr;
using ::blink::mojom::NativeIOErrorType;
namespace content {
namespace {
// Synchronous proxies to a wrapped NativeIOManager's methods.
class NativeIOManagerSync {
public:
// The caller must ensure that the NativeIOManager outlives this.
explicit NativeIOManagerSync(NativeIOManager* io_manager)
: io_manager_(io_manager) {}
NativeIOManagerSync(const NativeIOManagerSync&) = delete;
NativeIOManagerSync& operator=(const NativeIOManagerSync&) = delete;
blink::mojom::QuotaStatusCode DeleteStorageKeyData(
const StorageKey& storage_key) {
blink::mojom::QuotaStatusCode success_code;
base::RunLoop run_loop;
io_manager_->DeleteStorageKeyData(
storage_key, base::BindLambdaForTesting(
[&](blink::mojom::QuotaStatusCode returned_status) {
success_code = returned_status;
run_loop.Quit();
}));
run_loop.Run();
return success_code;
}
std::vector<StorageKey> GetStorageKeysForType(
blink::mojom::StorageType type) {
std::vector<StorageKey> storage_keys;
base::RunLoop run_loop;
io_manager_->GetStorageKeysForType(
type, base::BindLambdaForTesting(
[&](const std::vector<StorageKey>& returned_storage_keys) {
storage_keys = returned_storage_keys;
run_loop.Quit();
}));
run_loop.Run();
return storage_keys;
}
int64_t GetStorageKeyUsage(const StorageKey& storage_key,
blink::mojom::StorageType type) {
int64_t usage;
base::RunLoop run_loop;
io_manager_->GetStorageKeyUsage(
storage_key, type,
base::BindLambdaForTesting([&](int64_t returned_usage) {
usage = returned_usage;
run_loop.Quit();
}));
run_loop.Run();
return usage;
}
private:
const raw_ptr<NativeIOManager> io_manager_;
};
struct OpenFileResult {
base::File file;
uint64_t file_size;
NativeIOErrorPtr error;
};
#if BUILDFLAG(IS_MAC)
struct SetLengthResult {
base::File file;
int64_t actual_length;
NativeIOErrorPtr error;
};
#endif // BUILDFLAG(IS_MAC)
// Synchronous proxies to a wrapped NativeIOHost's methods.
class NativeIOHostSync {
public:
// The caller must ensure that the NativeIOHost outlives this.
explicit NativeIOHostSync(blink::mojom::NativeIOHost* io_host)
: io_host_(io_host) {}
NativeIOHostSync(const NativeIOHostSync&) = delete;
NativeIOHostSync& operator=(const NativeIOHostSync&) = delete;
~NativeIOHostSync() = default;
OpenFileResult OpenFile(
const std::string& name,
mojo::PendingReceiver<blink::mojom::NativeIOFileHost> file_receiver) {
OpenFileResult result;
base::RunLoop run_loop;
io_host_->OpenFile(name, std::move(file_receiver),
base::BindLambdaForTesting(
[&](base::File backend_file, uint64_t file_size,
NativeIOErrorPtr open_error) {
result.file = std::move(backend_file);
result.file_size = file_size;
result.error = std::move(open_error);
run_loop.Quit();
}));
run_loop.Run();
return result;
}
std::pair<NativeIOErrorPtr, uint64_t> DeleteFile(const std::string& name) {
NativeIOErrorPtr error;
uint64_t deleted_size;
base::RunLoop run_loop;
io_host_->DeleteFile(
name, base::BindLambdaForTesting([&](NativeIOErrorPtr delete_error,
uint64_t deleted_file_size) {
error = std::move(delete_error);
deleted_size = deleted_file_size;
run_loop.Quit();
}));
run_loop.Run();
return {std::move(error), deleted_size};
}
std::vector<std::string> GetAllFileNames() {
bool success;
std::vector<std::string> names;
base::RunLoop run_loop;
io_host_->GetAllFileNames(base::BindLambdaForTesting(
[&](bool backend_success,
const std::vector<std::string>& backend_names) {
success = backend_success;
names = backend_names;
run_loop.Quit();
}));
run_loop.Run();
return names;
}
NativeIOErrorPtr RenameFile(const std::string& old_name,
const std::string& new_name) {
base::RunLoop run_loop;
NativeIOErrorPtr error;
io_host_->RenameFile(
old_name, new_name,
base::BindLambdaForTesting([&](NativeIOErrorPtr rename_error) {
error = std::move(rename_error);
run_loop.Quit();
}));
run_loop.Run();
return error;
}
private:
const raw_ptr<blink::mojom::NativeIOHost> io_host_;
};
// Synchronous proxies to a wrapped NativeIOFileHost's methods.
class NativeIOFileHostSync {
public:
// The caller must ensure that the NativeIOFileHost outlives this.
explicit NativeIOFileHostSync(blink::mojom::NativeIOFileHost* file_host)
: file_host_(file_host) {}
NativeIOFileHostSync(const NativeIOFileHostSync&) = delete;
NativeIOFileHostSync& operator=(const NativeIOFileHostSync&) = delete;
~NativeIOFileHostSync() = default;
void Close() {
base::RunLoop run_loop;
file_host_->Close(run_loop.QuitClosure());
run_loop.Run();
return;
}
#if BUILDFLAG(IS_MAC)
SetLengthResult SetLength(const int64_t length, base::File file) {
SetLengthResult result;
base::RunLoop run_loop;
file_host_->SetLength(
length, std::move(file),
base::BindLambdaForTesting([&](base::File backend_file,
int64_t actual_length,
NativeIOErrorPtr set_length_error) {
result.file = std::move(backend_file);
result.actual_length = actual_length;
result.error = std::move(set_length_error);
run_loop.Quit();
}));
run_loop.Run();
return result;
}
#endif // BUILDFLAG(IS_MAC)
private:
const raw_ptr<blink::mojom::NativeIOFileHost> file_host_;
};
const char kExampleStorageKey[] = "https://example.com";
const char kGoogleStorageKey[] = "https://google.com";
class NativeIOManagerTest : public testing::TestWithParam<bool> {
public:
NativeIOManagerTest()
: special_storage_policy_(
base::MakeRefCounted<storage::MockSpecialStoragePolicy>()) {}
~NativeIOManagerTest() override = default;
void SetUp() override {
ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
quota_manager_ = base::MakeRefCounted<storage::MockQuotaManager>(
/*is_incognito=*/false, data_dir_.GetPath(),
base::SingleThreadTaskRunner::GetCurrentDefault(),
special_storage_policy_);
quota_manager_proxy_ = base::MakeRefCounted<storage::MockQuotaManagerProxy>(
quota_manager(), base::SingleThreadTaskRunner::GetCurrentDefault());
manager_ = std::make_unique<NativeIOManager>(data_dir_.GetPath(),
#if BUILDFLAG(IS_MAC)
allow_set_length_ipc(),
#endif // BUILDFLAG(IS_MAC)
special_storage_policy_,
quota_manager_proxy_);
manager_->BindReceiver(
StorageKey::CreateFromStringForTesting(kExampleStorageKey),
example_host_remote_.BindNewPipeAndPassReceiver(),
GetBadMessageCallback());
manager_->BindReceiver(
StorageKey::CreateFromStringForTesting(kGoogleStorageKey),
google_host_remote_.BindNewPipeAndPassReceiver(),
GetBadMessageCallback());
sync_manager_ =
std::make_unique<NativeIOManagerSync>(std::move(manager_.get()));
example_host_ =
std::make_unique<NativeIOHostSync>(example_host_remote_.get());
google_host_ =
std::make_unique<NativeIOHostSync>(google_host_remote_.get());
}
void TearDown() override {
// Let the client go away before dropping a ref of the quota manager proxy.
quota_manager_ = nullptr;
quota_manager_proxy_ = nullptr;
}
storage::MockQuotaManager* quota_manager() {
return static_cast<storage::MockQuotaManager*>(quota_manager_.get());
}
storage::MockQuotaManagerProxy* quota_manager_proxy() {
return static_cast<storage::MockQuotaManagerProxy*>(
quota_manager_proxy_.get());
}
mojo::ReportBadMessageCallback GetBadMessageCallback() {
return base::BindOnce(&NativeIOManagerTest::OnBadMessage,
base::Unretained(this));
}
void OnBadMessage(base::StringPiece reason) { NOTREACHED(); }
bool allow_set_length_ipc() { return GetParam(); }
protected:
struct Filename {
std::string name;
bool valid;
};
const std::vector<Filename> filenames_ = {
{"ascii", true},
{"_underscores_", true},
{std::string(99, 'x'), true},
{std::string(100, 'x'), true},
{"Uppercase", false},
{"Uppercase", false},
{"has-dash", false},
{"has.dot", false},
{"has/slash", false},
{std::string(101, 'x'), false},
{std::string(9999, 'x'), false},
};
scoped_refptr<storage::MockSpecialStoragePolicy> special_storage_policy_;
// This must be above NativeIOManager, to ensure that no file is accessed when
// the temporary directory is deleted.
base::ScopedTempDir data_dir_;
// These tests need a full TaskEnvironment because NativeIOHost uses the
// thread pool for file I/O.
base::test::TaskEnvironment task_environment_;
scoped_refptr<storage::QuotaManager> quota_manager_;
scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
// The NativeIOManager is on the heap because it requires the profile path at
// construction, and we only know the path during SetUp.
std::unique_ptr<NativeIOManager> manager_;
std::unique_ptr<NativeIOManagerSync> sync_manager_;
// Hosts for two different storage_keys, used for isolation testing.
mojo::Remote<blink::mojom::NativeIOHost> example_host_remote_;
mojo::Remote<blink::mojom::NativeIOHost> google_host_remote_;
std::unique_ptr<NativeIOHostSync> example_host_;
std::unique_ptr<NativeIOHostSync> google_host_;
};
TEST_P(NativeIOManagerTest, DefaultBucketCreatedOnBindReceiver) {
EXPECT_THAT(google_host_->GetAllFileNames(), testing::SizeIs(0));
storage::QuotaManagerProxySync quota_manager_proxy_sync(
quota_manager_proxy());
// Check default bucket exists for https://example.com.
storage::QuotaErrorOr<storage::BucketInfo> result =
quota_manager_proxy_sync.GetBucket(
StorageKey::CreateFromStringForTesting(kExampleStorageKey),
storage::kDefaultBucketName, blink::mojom::StorageType::kTemporary);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result->name, storage::kDefaultBucketName);
EXPECT_EQ(result->storage_key,
StorageKey::CreateFromStringForTesting(kExampleStorageKey));
EXPECT_GT(result->id.value(), 0);
// Check default bucket exists for https://google.com.
result = quota_manager_proxy_sync.GetBucket(
StorageKey::CreateFromStringForTesting(kGoogleStorageKey),
storage::kDefaultBucketName, blink::mojom::StorageType::kTemporary);
EXPECT_TRUE(result.ok());
EXPECT_EQ(result->name, storage::kDefaultBucketName);
EXPECT_EQ(result->storage_key,
StorageKey::CreateFromStringForTesting(kGoogleStorageKey));
EXPECT_GT(result->id.value(), 0);
}
TEST_P(NativeIOManagerTest, OpenFile_Names) {
for (const Filename& filename : filenames_) {
mojo::test::BadMessageObserver bad_message_observer;
mojo::Remote<blink::mojom::NativeIOFileHost> file_host;
OpenFileResult result = example_host_->OpenFile(
filename.name, file_host.BindNewPipeAndPassReceiver());
EXPECT_EQ(result.file.IsValid(), filename.valid);
if (!filename.valid) {
EXPECT_EQ(result.file_size, 0u);
EXPECT_EQ(result.error->type, NativeIOErrorType::kUnknown);
EXPECT_EQ("Invalid file name", bad_message_observer.WaitForBadMessage());
}
}
}
TEST_P(NativeIOManagerTest, OpenFile_Locks_OpenFile) {
mojo::Remote<blink::mojom::NativeIOFileHost> file_host_remote;
base::File file =
example_host_
->OpenFile("test_file", file_host_remote.BindNewPipeAndPassReceiver())
.file;
EXPECT_TRUE(file.IsValid());
mojo::Remote<blink::mojom::NativeIOFileHost> locked_file_host_remote;
OpenFileResult locked_result = example_host_->OpenFile(
"test_file", locked_file_host_remote.BindNewPipeAndPassReceiver());
EXPECT_FALSE(locked_result.file.IsValid());
EXPECT_EQ(locked_result.error->type,
NativeIOErrorType::kNoModificationAllowed)
<< "A file cannot be opened twice";
}
TEST_P(NativeIOManagerTest, OpenFile_SameName) {
const std::string kTestData("Test Data");
mojo::Remote<blink::mojom::NativeIOFileHost> file_host_remote;
base::File file =
example_host_
->OpenFile("test_file", file_host_remote.BindNewPipeAndPassReceiver())
.file;
EXPECT_TRUE(file.IsValid());
EXPECT_EQ(static_cast<int>(kTestData.size()),
file.Write(0, kTestData.data(), kTestData.size()));
file.Close();
NativeIOFileHostSync file_host(file_host_remote.get());
file_host.Close();
mojo::Remote<blink::mojom::NativeIOFileHost> same_file_host_remote;
OpenFileResult same_result = example_host_->OpenFile(
"test_file", same_file_host_remote.BindNewPipeAndPassReceiver());
EXPECT_TRUE(same_result.file.IsValid());
EXPECT_EQ(same_result.file_size, static_cast<uint64_t>(kTestData.size()));
char read_buffer[kTestData.size()];
EXPECT_EQ(static_cast<int>(kTestData.size()),
same_result.file.Read(0, read_buffer, kTestData.size()));
EXPECT_EQ(kTestData, std::string(read_buffer, kTestData.size()));
same_result.file.Close();
}
TEST_P(NativeIOManagerTest, DeleteFile_Names) {
for (const Filename& filename : filenames_) {
if (filename.valid) {
EXPECT_EQ(example_host_->DeleteFile(filename.name).first->type,
NativeIOErrorType::kSuccess);
} else {
mojo::test::BadMessageObserver bad_message_observer;
EXPECT_EQ(example_host_->DeleteFile(filename.name).first->type,
NativeIOErrorType::kUnknown);
EXPECT_EQ("Invalid file name", bad_message_observer.WaitForBadMessage());
}
}
}
TEST_P(NativeIOManagerTest, OpenFile_Locks_DeleteFile) {
mojo::Remote<blink::mojom::NativeIOFileHost> file_host;
base::File file =
example_host_
->OpenFile("test_file", file_host.BindNewPipeAndPassReceiver())
.file;
EXPECT_TRUE(file.IsValid());
EXPECT_EQ(example_host_->DeleteFile("test_file").first->type,
NativeIOErrorType::kNoModificationAllowed);
}
TEST_P(NativeIOManagerTest, OpenFile_Locks_RenameFile) {
mojo::Remote<blink::mojom::NativeIOFileHost> file_host;
base::File file =
example_host_
->OpenFile("test_file_in_use", file_host.BindNewPipeAndPassReceiver())
.file;
EXPECT_TRUE(file.IsValid());
mojo::Remote<blink::mojom::NativeIOFileHost> file_host2;
base::File file_closed =
example_host_
->OpenFile("test_file_closed",
file_host2.BindNewPipeAndPassReceiver())
.file;
EXPECT_TRUE(file_closed.IsValid());
file_closed.Close();
NativeIOFileHostSync file_host2_sync(file_host2.get());
file_host2_sync.Close();
EXPECT_EQ(
example_host_->RenameFile("test_file_in_use", "renamed_test_file")->type,
NativeIOErrorType::kNoModificationAllowed)
<< "An open file cannot be renamed";
EXPECT_EQ(
example_host_->RenameFile("test_file_closed", "test_file_in_use")->type,
NativeIOErrorType::kNoModificationAllowed)
<< "An open file cannot be overwritten";
;
}
TEST_P(NativeIOManagerTest, DeleteFile_WipesData) {
const std::string kTestData("Test Data");
mojo::Remote<blink::mojom::NativeIOFileHost> file_host_remote;
base::File file =
example_host_
->OpenFile("test_file", file_host_remote.BindNewPipeAndPassReceiver())
.file;
EXPECT_TRUE(file.IsValid());
EXPECT_EQ(static_cast<int>(kTestData.size()),
file.Write(0, kTestData.data(), kTestData.size()));
file.Close();
NativeIOFileHostSync file_host(file_host_remote.get());
file_host.Close();
EXPECT_EQ(example_host_->DeleteFile("test_file").first->type,
NativeIOErrorType::kSuccess);
mojo::Remote<blink::mojom::NativeIOFileHost> same_file_host_remote;
base::File same_file =
example_host_
->OpenFile("test_file",
same_file_host_remote.BindNewPipeAndPassReceiver())
.file;
EXPECT_TRUE(same_file.IsValid());
char read_buffer[kTestData.size()];
EXPECT_EQ(0, same_file.Read(0, read_buffer, kTestData.size()));
}
TEST_P(NativeIOManagerTest, DeleteFile_ReportsLengths) {
const std::string kTestData("Test Data");
mojo::Remote<blink::mojom::NativeIOFileHost> file_host_remote;
base::File file =
example_host_
->OpenFile("test_file", file_host_remote.BindNewPipeAndPassReceiver())
.file;
EXPECT_TRUE(file.IsValid());
EXPECT_EQ(static_cast<int>(kTestData.size()),
file.Write(0, kTestData.data(), kTestData.size()));
file.Close();
NativeIOFileHostSync file_host(file_host_remote.get());
file_host.Close();
mojo::Remote<blink::mojom::NativeIOFileHost> same_file_host_remote;
OpenFileResult same_file_result = example_host_->OpenFile(
"test_file", same_file_host_remote.BindNewPipeAndPassReceiver());
EXPECT_TRUE(same_file_result.file.IsValid());
ASSERT_EQ(same_file_result.file_size,
static_cast<uint64_t>(kTestData.size()));
EXPECT_EQ(same_file_result.error->type, NativeIOErrorType::kSuccess);
same_file_result.file.Close();
NativeIOFileHostSync same_file_host(same_file_host_remote.get());
same_file_host.Close();
std::pair<NativeIOErrorPtr, uint64_t> delete_result =
example_host_->DeleteFile("test_file");
EXPECT_EQ(delete_result.first->type, NativeIOErrorType::kSuccess);
EXPECT_EQ(delete_result.second, static_cast<uint64_t>(kTestData.size()));
}
TEST_P(NativeIOManagerTest, GetAllFiles_Empty) {
std::vector<std::string> file_names = example_host_->GetAllFileNames();
EXPECT_EQ(0u, file_names.size());
}
TEST_P(NativeIOManagerTest, GetAllFiles_AfterOpen) {
mojo::Remote<blink::mojom::NativeIOFileHost> file_host_remote;
base::File file =
example_host_
->OpenFile("test_file", file_host_remote.BindNewPipeAndPassReceiver())
.file;
file.Close();
NativeIOFileHostSync file_host(file_host_remote.get());
file_host.Close();
std::vector<std::string> file_names = example_host_->GetAllFileNames();
EXPECT_EQ(1u, file_names.size());
EXPECT_EQ("test_file", file_names[0]);
}
TEST_P(NativeIOManagerTest, RenameFile_AfterOpenAndRename) {
mojo::Remote<blink::mojom::NativeIOFileHost> file_host_remote;
base::File file =
example_host_
->OpenFile("test_file", file_host_remote.BindNewPipeAndPassReceiver())
.file;
file.Close();
NativeIOFileHostSync file_host(file_host_remote.get());
file_host.Close();
example_host_->RenameFile("test_file", "renamed_test_file");
std::vector<std::string> file_names = example_host_->GetAllFileNames();
EXPECT_EQ(1u, file_names.size());
EXPECT_EQ("renamed_test_file", file_names[0]);
}
TEST_P(NativeIOManagerTest, RenameFile_Names) {
mojo::Remote<blink::mojom::NativeIOFileHost> file_host_remote;
base::File file =
example_host_
->OpenFile("test_file", file_host_remote.BindNewPipeAndPassReceiver())
.file;
file.Close();
NativeIOFileHostSync file_host(file_host_remote.get());
file_host.Close();
for (const Filename& filename : filenames_) {
if (filename.valid) {
EXPECT_EQ(example_host_->RenameFile("test_file", filename.name)->type,
NativeIOErrorType::kSuccess);
EXPECT_EQ(example_host_->RenameFile(filename.name, "inexistant_test_file")
->type,
NativeIOErrorType::kSuccess);
// Return to initial state
EXPECT_EQ(
example_host_->RenameFile("inexistant_test_file", "test_file")->type,
NativeIOErrorType::kSuccess);
} else {
mojo::test::BadMessageObserver bad_message_observer;
EXPECT_EQ(example_host_->RenameFile("test_file", filename.name)->type,
NativeIOErrorType::kUnknown);
EXPECT_EQ("Invalid file name", bad_message_observer.WaitForBadMessage());
EXPECT_EQ(example_host_->RenameFile(filename.name, "inexistant_test_file")
->type,
NativeIOErrorType::kUnknown);
EXPECT_EQ("Invalid file name", bad_message_observer.WaitForBadMessage());
}
}
}
#if BUILDFLAG(IS_MAC)
TEST_P(NativeIOManagerTest, SetLength) {
const std::string kTestData("Test Data");
const int kTestDataSize = kTestData.size();
const int kTruncatedSize = 4;
mojo::Remote<blink::mojom::NativeIOFileHost> file_host_remote;
base::File file =
example_host_
->OpenFile("test_file", file_host_remote.BindNewPipeAndPassReceiver())
.file;
EXPECT_TRUE(file.IsValid());
EXPECT_EQ(kTestDataSize, file.Write(0, kTestData.data(), kTestDataSize));
NativeIOFileHostSync file_host(file_host_remote.get());
SetLengthResult set_length_result;
if (allow_set_length_ipc()) {
set_length_result = file_host.SetLength(kTruncatedSize, std::move(file));
EXPECT_EQ(set_length_result.error->type, NativeIOErrorType::kSuccess);
EXPECT_EQ(set_length_result.actual_length, kTruncatedSize);
} else {
mojo::test::BadMessageObserver bad_message_observer;
set_length_result = file_host.SetLength(kTruncatedSize, std::move(file));
EXPECT_EQ(set_length_result.error->type, NativeIOErrorType::kUnknown);
EXPECT_EQ("SetLength() disabled on this OS.",
bad_message_observer.WaitForBadMessage());
EXPECT_EQ(set_length_result.actual_length, 0);
}
file = std::move(set_length_result.file);
EXPECT_TRUE(file.IsValid());
char read_buffer[kTestData.size()];
EXPECT_EQ(allow_set_length_ipc() ? kTruncatedSize : kTestDataSize,
file.Read(0, read_buffer, kTestData.size()));
}
TEST_P(NativeIOManagerTest, SetLength_NegativeLength) {
mojo::Remote<blink::mojom::NativeIOFileHost> file_host_remote;
base::File file =
example_host_
->OpenFile("test_file", file_host_remote.BindNewPipeAndPassReceiver())
.file;
NativeIOFileHostSync file_host(file_host_remote.get());
mojo::test::BadMessageObserver bad_message_observer;
SetLengthResult set_length_result = file_host.SetLength(-5, std::move(file));
EXPECT_EQ(set_length_result.error->type, NativeIOErrorType::kUnknown);
EXPECT_EQ(allow_set_length_ipc() ? "The file length cannot be negative."
: "SetLength() disabled on this OS.",
bad_message_observer.WaitForBadMessage());
}
#endif // BUILDFLAG(IS_MAC)
TEST_P(NativeIOManagerTest, StorageKeyIsolation) {
const std::string kTestData("Test Data");
mojo::Remote<blink::mojom::NativeIOFileHost> file_host_remote;
base::File file =
google_host_
->OpenFile("test_file", file_host_remote.BindNewPipeAndPassReceiver())
.file;
EXPECT_TRUE(file.IsValid());
EXPECT_EQ(static_cast<int>(kTestData.size()),
file.Write(0, kTestData.data(), kTestData.size()));
file.Close();
NativeIOFileHostSync file_host(file_host_remote.get());
file_host.Close();
std::vector<std::string> file_names = google_host_->GetAllFileNames();
EXPECT_EQ(1u, file_names.size());
std::vector<std::string> other_names = example_host_->GetAllFileNames();
EXPECT_EQ(0u, other_names.size());
mojo::Remote<blink::mojom::NativeIOFileHost> same_file_host_remote;
base::File same_file =
example_host_
->OpenFile("test_file",
same_file_host_remote.BindNewPipeAndPassReceiver())
.file;
EXPECT_TRUE(same_file.IsValid());
char read_buffer[kTestData.size()];
EXPECT_EQ(0, same_file.Read(0, read_buffer, kTestData.size()));
}
TEST_P(NativeIOManagerTest, BindReceiver_UntrustworthyStorageKey) {
mojo::Remote<blink::mojom::NativeIOHost> insecure_host_remote_;
mojo::FakeMessageDispatchContext fake_dispatch_context;
mojo::test::BadMessageObserver bad_message_observer;
manager_->BindReceiver(
StorageKey::CreateFromStringForTesting("http://insecure.com"),
insecure_host_remote_.BindNewPipeAndPassReceiver(),
mojo::GetBadMessageCallback());
EXPECT_EQ("Called NativeIO from an insecure context",
bad_message_observer.WaitForBadMessage());
}
TEST_P(NativeIOManagerTest, DeleteStorageKeyData_UnsupportedStorageKey) {
mojo::Remote<blink::mojom::NativeIOFileHost> example_host_remote;
base::File example_file =
example_host_
->OpenFile("test_file",
example_host_remote.BindNewPipeAndPassReceiver())
.file;
EXPECT_TRUE(example_file.IsValid());
example_file.Close();
NativeIOFileHostSync example_file_host(example_host_remote.get());
example_file_host.Close();
StorageKey insecure_storage_key =
StorageKey::CreateFromStringForTesting("http://insecure.com");
EXPECT_EQ(sync_manager_->DeleteStorageKeyData(insecure_storage_key),
blink::mojom::QuotaStatusCode::kOk);
EXPECT_TRUE(base::PathExists(manager_->RootPathForStorageKey(
StorageKey::CreateFromStringForTesting(kExampleStorageKey))));
}
TEST_P(NativeIOManagerTest, DeleteStorageKeyData_StorageKeyWithNoData) {
mojo::Remote<blink::mojom::NativeIOFileHost> example_host_remote;
base::File example_file =
example_host_
->OpenFile("test_file",
example_host_remote.BindNewPipeAndPassReceiver())
.file;
EXPECT_TRUE(example_file.IsValid());
example_file.Close();
NativeIOFileHostSync example_file_host(example_host_remote.get());
example_file_host.Close();
StorageKey storage_key_with_no_data =
StorageKey::CreateFromStringForTesting("https://other.example.com");
EXPECT_EQ(sync_manager_->DeleteStorageKeyData(storage_key_with_no_data),
blink::mojom::QuotaStatusCode::kOk);
EXPECT_TRUE(base::PathExists(manager_->RootPathForStorageKey(
StorageKey::CreateFromStringForTesting(kExampleStorageKey))));
}
TEST_P(NativeIOManagerTest, DeleteStorageKeyData_ConcurrentDeletion) {
{
mojo::Remote<blink::mojom::NativeIOFileHost> example_host_remote;
base::File example_file =
example_host_
->OpenFile("test_file",
example_host_remote.BindNewPipeAndPassReceiver())
.file;
EXPECT_TRUE(example_file.IsValid());
example_file.Close();
NativeIOFileHostSync example_file_host(example_host_remote.get());
example_file_host.Close();
}
// Reset the last mojo connection to the example host, so the host remains
// without connections during deletion.
example_host_ = nullptr;
example_host_remote_.reset();
StorageKey example_storage_key =
StorageKey::CreateFromStringForTesting(kExampleStorageKey);
base::RunLoop delete_run_loop;
blink::mojom::QuotaStatusCode delete_status;
manager_->DeleteStorageKeyData(
example_storage_key,
base::BindLambdaForTesting([&](blink::mojom::QuotaStatusCode status) {
delete_run_loop.Quit();
delete_status = status;
}));
EXPECT_EQ(sync_manager_->DeleteStorageKeyData(example_storage_key),
blink::mojom::QuotaStatusCode::kOk);
delete_run_loop.Run();
EXPECT_EQ(delete_status, blink::mojom::QuotaStatusCode::kOk);
EXPECT_TRUE(
!base::PathExists(manager_->RootPathForStorageKey(example_storage_key)));
}
TEST_P(NativeIOManagerTest, GetStorageKeysByType_Empty) {
std::vector<StorageKey> storage_keys = sync_manager_->GetStorageKeysForType(
blink::mojom::StorageType::kTemporary);
EXPECT_EQ(0u, storage_keys.size());
}
TEST_P(NativeIOManagerTest, GetStorageKeysByType_ReturnsInactiveStorageKeys) {
mojo::Remote<blink::mojom::NativeIOFileHost> example_host_remote;
base::File example_file =
example_host_
->OpenFile("test_file",
example_host_remote.BindNewPipeAndPassReceiver())
.file;
example_file.Close();
NativeIOFileHostSync example_file_host(example_host_remote.get());
example_file_host.Close();
std::vector<StorageKey> storage_keys = sync_manager_->GetStorageKeysForType(
blink::mojom::StorageType::kTemporary);
EXPECT_EQ(1u, storage_keys.size());
EXPECT_EQ(StorageKey::CreateFromStringForTesting(kExampleStorageKey),
storage_keys[0]);
}
TEST_P(NativeIOManagerTest, GetStorageKeysByType_ReturnsActiveStorageKeys) {
mojo::Remote<blink::mojom::NativeIOFileHost> example_host_remote;
base::File example_file =
example_host_
->OpenFile("test_file",
example_host_remote.BindNewPipeAndPassReceiver())
.file;
std::vector<StorageKey> storage_keys = sync_manager_->GetStorageKeysForType(
blink::mojom::StorageType::kTemporary);
EXPECT_EQ(1u, storage_keys.size());
EXPECT_EQ(StorageKey::CreateFromStringForTesting(kExampleStorageKey),
storage_keys[0]);
EXPECT_TRUE(example_file.IsValid());
example_file.Close();
NativeIOFileHostSync example_file_host(example_host_remote.get());
example_file_host.Close();
}
TEST_P(NativeIOManagerTest, GetStorageKeyUsage_ActiveStorageKeyUsage) {
mojo::Remote<blink::mojom::NativeIOFileHost> example_host_remote;
base::File example_file =
example_host_
->OpenFile("test_file",
example_host_remote.BindNewPipeAndPassReceiver())
.file;
int64_t expected_usage = 100;
example_file.SetLength(expected_usage);
example_file.Close();
NativeIOFileHostSync example_file_host(example_host_remote.get());
example_file_host.Close();
int64_t usage = sync_manager_->GetStorageKeyUsage(
StorageKey::CreateFromStringForTesting(kExampleStorageKey),
blink::mojom::StorageType::kTemporary);
EXPECT_EQ(expected_usage, usage);
}
TEST_P(NativeIOManagerTest, GetStorageKeyUsage_NonexistingStorageKeyUsage) {
int64_t usage = sync_manager_->GetStorageKeyUsage(
StorageKey::CreateFromStringForTesting(kExampleStorageKey),
blink::mojom::StorageType::kTemporary);
EXPECT_EQ(0u, usage);
}
TEST_P(NativeIOManagerTest, IncognitoQuota) {
auto quota_manager = base::MakeRefCounted<storage::MockQuotaManager>(
/*is_incognito=*/true, base::FilePath(),
base::SingleThreadTaskRunner::GetCurrentDefault().get(),
special_storage_policy_);
auto quota_manager_proxy =
base::MakeRefCounted<storage::MockQuotaManagerProxy>(
quota_manager.get(),
base::SingleThreadTaskRunner::GetCurrentDefault());
auto manager = std::make_unique<NativeIOManager>(base::FilePath(),
#if BUILDFLAG(IS_MAC)
allow_set_length_ipc(),
#endif // BUILDFLAG(IS_MAC)
special_storage_policy_,
quota_manager_proxy);
auto sync_manager = std::make_unique<NativeIOManagerSync>(manager.get());
EXPECT_THAT(sync_manager->GetStorageKeysForType(
blink::mojom::StorageType::kTemporary),
testing::SizeIs(0));
EXPECT_EQ(0, sync_manager->GetStorageKeyUsage(
StorageKey::CreateFromStringForTesting(kExampleStorageKey),
blink::mojom::StorageType::kTemporary));
}
INSTANTIATE_TEST_SUITE_P(,
NativeIOManagerTest,
::testing::Values(
#if BUILDFLAG(IS_MAC)
false,
true
#else // !BUILDFLAG(IS_MAC)
false
#endif // BUILDFLAG(IS_MAC)
));
} // namespace
} // namespace content