blob: ffafe3d8a4adff5fbd936e6cd128f0af470e7422 [file] [log] [blame]
// Copyright 2017 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 "storage/browser/blob/blob_url_store_impl.h"
#include "base/bind.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "storage/browser/blob/blob_data_builder.h"
#include "storage/browser/blob/blob_impl.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "storage/browser/test/mock_blob_registry_delegate.h"
#include "testing/gtest/include/gtest/gtest.h"
using blink::mojom::BlobPtr;
using blink::mojom::BlobURLStore;
using blink::mojom::BlobURLStorePtr;
namespace storage {
class BlobURLStoreImplTest : public testing::Test {
public:
void SetUp() override {
context_ = std::make_unique<BlobStorageContext>();
mojo::core::SetDefaultProcessErrorCallback(base::BindRepeating(
&BlobURLStoreImplTest::OnBadMessage, base::Unretained(this)));
}
void TearDown() override {
mojo::core::SetDefaultProcessErrorCallback(
mojo::core::ProcessErrorCallback());
}
void OnBadMessage(const std::string& error) {
bad_messages_.push_back(error);
}
BlobPtr CreateBlobFromString(const std::string& uuid,
const std::string& contents) {
auto builder = std::make_unique<BlobDataBuilder>(uuid);
builder->set_content_type("text/plain");
builder->AppendData(contents);
BlobPtr blob;
BlobImpl::Create(context_->AddFinishedBlob(std::move(builder)),
MakeRequest(&blob));
return blob;
}
std::string UUIDFromBlob(blink::mojom::Blob* blob) {
base::RunLoop loop;
std::string received_uuid;
blob->GetInternalUUID(base::BindOnce(
[](base::OnceClosure quit_closure, std::string* uuid_out,
const std::string& uuid) {
*uuid_out = uuid;
std::move(quit_closure).Run();
},
loop.QuitClosure(), &received_uuid));
loop.Run();
return received_uuid;
}
BlobURLStorePtr CreateURLStore() {
BlobURLStorePtr result;
mojo::MakeStrongBinding(
std::make_unique<BlobURLStoreImpl>(context_->AsWeakPtr(), &delegate_),
MakeRequest(&result));
return result;
}
void RegisterURL(BlobURLStore* store, BlobPtr blob, const GURL& url) {
base::RunLoop loop;
store->Register(std::move(blob), url, loop.QuitClosure());
loop.Run();
}
BlobPtr ResolveURL(BlobURLStore* store, const GURL& url) {
BlobPtr result;
base::RunLoop loop;
store->Resolve(kValidUrl, base::BindOnce(
[](base::OnceClosure done, BlobPtr* blob_out,
BlobPtr blob) {
*blob_out = std::move(blob);
std::move(done).Run();
},
loop.QuitClosure(), &result));
loop.Run();
return result;
}
const std::string kId = "id";
const GURL kValidUrl = GURL("blob:id");
const GURL kInvalidUrl = GURL("bolb:id");
const GURL kFragmentUrl = GURL("blob:id#fragment");
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
std::unique_ptr<BlobStorageContext> context_;
MockBlobRegistryDelegate delegate_;
std::vector<std::string> bad_messages_;
};
TEST_F(BlobURLStoreImplTest, BasicRegisterRevoke) {
BlobPtr blob = CreateBlobFromString(kId, "hello world");
// Register a URL and make sure the URL keeps the blob alive.
BlobURLStoreImpl url_store(context_->AsWeakPtr(), &delegate_);
RegisterURL(&url_store, std::move(blob), kValidUrl);
std::unique_ptr<BlobDataHandle> blob_data_handle =
context_->GetBlobDataFromPublicURL(kValidUrl);
ASSERT_TRUE(blob_data_handle);
EXPECT_EQ(kId, blob_data_handle->uuid());
blob_data_handle = nullptr;
// Revoke the URL.
url_store.Revoke(kValidUrl);
blob_data_handle = context_->GetBlobDataFromPublicURL(kValidUrl);
EXPECT_FALSE(blob_data_handle);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(context_->registry().HasEntry(kId));
}
TEST_F(BlobURLStoreImplTest, RegisterInvalidScheme) {
BlobPtr blob = CreateBlobFromString(kId, "hello world");
BlobURLStorePtr url_store = CreateURLStore();
RegisterURL(url_store.get(), std::move(blob), kInvalidUrl);
EXPECT_FALSE(context_->GetBlobDataFromPublicURL(kInvalidUrl));
EXPECT_EQ(1u, bad_messages_.size());
}
TEST_F(BlobURLStoreImplTest, RegisterCantCommit) {
BlobPtr blob = CreateBlobFromString(kId, "hello world");
delegate_.can_commit_url_result = false;
BlobURLStorePtr url_store = CreateURLStore();
RegisterURL(url_store.get(), std::move(blob), kValidUrl);
EXPECT_FALSE(context_->GetBlobDataFromPublicURL(kValidUrl));
EXPECT_EQ(1u, bad_messages_.size());
}
TEST_F(BlobURLStoreImplTest, RegisterUrlFragment) {
BlobPtr blob = CreateBlobFromString(kId, "hello world");
BlobURLStorePtr url_store = CreateURLStore();
RegisterURL(url_store.get(), std::move(blob), kFragmentUrl);
EXPECT_FALSE(context_->GetBlobDataFromPublicURL(kFragmentUrl));
EXPECT_EQ(1u, bad_messages_.size());
}
TEST_F(BlobURLStoreImplTest, ImplicitRevoke) {
const GURL kValidUrl2("blob:id2");
BlobPtr blob = CreateBlobFromString(kId, "hello world");
BlobPtr blob2;
blob->Clone(MakeRequest(&blob2));
auto url_store =
std::make_unique<BlobURLStoreImpl>(context_->AsWeakPtr(), &delegate_);
RegisterURL(url_store.get(), std::move(blob), kValidUrl);
EXPECT_TRUE(context_->GetBlobDataFromPublicURL(kValidUrl));
RegisterURL(url_store.get(), std::move(blob2), kValidUrl2);
EXPECT_TRUE(context_->GetBlobDataFromPublicURL(kValidUrl2));
// Destroy URL Store, should revoke URLs.
url_store = nullptr;
EXPECT_FALSE(context_->GetBlobDataFromPublicURL(kValidUrl));
EXPECT_FALSE(context_->GetBlobDataFromPublicURL(kValidUrl2));
}
TEST_F(BlobURLStoreImplTest, RevokeThroughDifferentURLStore) {
BlobPtr blob = CreateBlobFromString(kId, "hello world");
BlobURLStoreImpl url_store1(context_->AsWeakPtr(), &delegate_);
BlobURLStoreImpl url_store2(context_->AsWeakPtr(), &delegate_);
RegisterURL(&url_store1, std::move(blob), kValidUrl);
EXPECT_TRUE(context_->GetBlobDataFromPublicURL(kValidUrl));
url_store2.Revoke(kValidUrl);
EXPECT_FALSE(context_->GetBlobDataFromPublicURL(kValidUrl));
}
TEST_F(BlobURLStoreImplTest, RevokeInvalidScheme) {
BlobURLStorePtr url_store = CreateURLStore();
url_store->Revoke(kInvalidUrl);
url_store.FlushForTesting();
EXPECT_EQ(1u, bad_messages_.size());
}
TEST_F(BlobURLStoreImplTest, RevokeCantCommit) {
delegate_.can_commit_url_result = false;
BlobURLStorePtr url_store = CreateURLStore();
url_store->Revoke(kValidUrl);
url_store.FlushForTesting();
EXPECT_EQ(1u, bad_messages_.size());
}
TEST_F(BlobURLStoreImplTest, RevokeCantCommit_ProcessNotValid) {
delegate_.can_commit_url_result = false;
delegate_.is_process_valid_result = false;
BlobURLStorePtr url_store = CreateURLStore();
url_store->Revoke(kValidUrl);
url_store.FlushForTesting();
EXPECT_TRUE(bad_messages_.empty());
EXPECT_FALSE(context_->GetBlobDataFromPublicURL(kValidUrl));
}
TEST_F(BlobURLStoreImplTest, RevokeURLWithFragment) {
BlobURLStorePtr url_store = CreateURLStore();
url_store->Revoke(kFragmentUrl);
url_store.FlushForTesting();
EXPECT_EQ(1u, bad_messages_.size());
}
TEST_F(BlobURLStoreImplTest, Resolve) {
BlobPtr blob = CreateBlobFromString(kId, "hello world");
BlobURLStoreImpl url_store(context_->AsWeakPtr(), &delegate_);
RegisterURL(&url_store, std::move(blob), kValidUrl);
blob = ResolveURL(&url_store, kValidUrl);
ASSERT_TRUE(blob);
EXPECT_EQ(kId, UUIDFromBlob(blob.get()));
blob = ResolveURL(&url_store, kFragmentUrl);
ASSERT_TRUE(blob);
EXPECT_EQ(kId, UUIDFromBlob(blob.get()));
}
TEST_F(BlobURLStoreImplTest, ResolveNonExistentURL) {
BlobURLStoreImpl url_store(context_->AsWeakPtr(), &delegate_);
BlobPtr blob = ResolveURL(&url_store, kValidUrl);
EXPECT_FALSE(blob);
blob = ResolveURL(&url_store, kFragmentUrl);
EXPECT_FALSE(blob);
}
TEST_F(BlobURLStoreImplTest, ResolveInvalidURL) {
BlobURLStoreImpl url_store(context_->AsWeakPtr(), &delegate_);
BlobPtr blob = ResolveURL(&url_store, kInvalidUrl);
EXPECT_FALSE(blob);
}
TEST_F(BlobURLStoreImplTest, ResolveAsURLLoaderFactory) {
BlobPtr blob = CreateBlobFromString(kId, "hello world");
BlobURLStoreImpl url_store(context_->AsWeakPtr(), &delegate_);
RegisterURL(&url_store, std::move(blob), kValidUrl);
network::mojom::URLLoaderFactoryPtr factory;
url_store.ResolveAsURLLoaderFactory(kValidUrl, MakeRequest(&factory));
auto request = std::make_unique<network::ResourceRequest>();
request->url = kValidUrl;
auto loader = network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
base::RunLoop loop;
loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
factory.get(), base::BindLambdaForTesting(
[&](std::unique_ptr<std::string> response_body) {
loop.Quit();
ASSERT_TRUE(response_body);
EXPECT_EQ("hello world", *response_body);
}));
loop.Run();
}
TEST_F(BlobURLStoreImplTest, ResolveForNavigation) {
BlobPtr blob = CreateBlobFromString(kId, "hello world");
BlobURLStoreImpl url_store(context_->AsWeakPtr(), &delegate_);
RegisterURL(&url_store, std::move(blob), kValidUrl);
blink::mojom::BlobURLTokenPtr token_ptr;
url_store.ResolveForNavigation(kValidUrl, MakeRequest(&token_ptr));
base::UnguessableToken token;
base::RunLoop loop;
token_ptr->GetToken(base::BindLambdaForTesting(
[&](const base::UnguessableToken& received_token) {
token = received_token;
loop.Quit();
}));
loop.Run();
GURL blob_url;
std::string blob_uuid;
EXPECT_TRUE(
context_->registry().GetTokenMapping(token, &blob_url, &blob_uuid));
EXPECT_EQ(kValidUrl, blob_url);
EXPECT_EQ(kId, blob_uuid);
}
} // namespace storage