[go: up one dir, main page]

blob: 04c44794d55e3b3c1c2b9ec7eae2201fabd9f796 [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/callback_helpers.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/unguessable_token.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "mojo/public/cpp/system/functions.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/resource_request.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/blob/blob_url_registry.h"
#include "testing/gtest/include/gtest/gtest.h"
using blink::mojom::BlobURLStore;
namespace storage {
class BlobURLStoreImplTest : public testing::Test {
public:
void SetUp() override {
context_ = std::make_unique<BlobStorageContext>();
agent_cluster_id_ = base::UnguessableToken::Create();
mojo::SetDefaultProcessErrorHandler(base::BindRepeating(
&BlobURLStoreImplTest::OnBadMessage, base::Unretained(this)));
}
void TearDown() override {
mojo::SetDefaultProcessErrorHandler(base::NullCallback());
}
void OnBadMessage(const std::string& error) {
bad_messages_.push_back(error);
}
mojo::PendingRemote<blink::mojom::Blob> 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);
mojo::PendingRemote<blink::mojom::Blob> blob;
BlobImpl::Create(context_->AddFinishedBlob(std::move(builder)),
blob.InitWithNewPipeAndPassReceiver());
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;
}
mojo::PendingRemote<BlobURLStore> CreateURLStore() {
mojo::PendingRemote<BlobURLStore> result;
mojo::MakeSelfOwnedReceiver(
std::make_unique<BlobURLStoreImpl>(kOrigin, url_registry_.AsWeakPtr()),
result.InitWithNewPipeAndPassReceiver());
return result;
}
void RegisterURL(BlobURLStore* store,
mojo::PendingRemote<blink::mojom::Blob> blob,
const GURL& url) {
base::RunLoop loop;
store->Register(std::move(blob), url, agent_cluster_id_,
loop.QuitClosure());
loop.Run();
}
mojo::PendingRemote<blink::mojom::Blob> ResolveURL(BlobURLStore* store,
const GURL& url) {
mojo::PendingRemote<blink::mojom::Blob> result;
base::RunLoop loop;
store->Resolve(kValidUrl,
base::BindOnce(
[](base::OnceClosure done,
mojo::PendingRemote<blink::mojom::Blob>* blob_out,
const base::UnguessableToken& agent_registered,
mojo::PendingRemote<blink::mojom::Blob> blob,
const absl::optional<base::UnguessableToken>&
unsafe_agent_cluster_id) {
if (blob)
EXPECT_EQ(agent_registered, unsafe_agent_cluster_id);
*blob_out = std::move(blob);
std::move(done).Run();
},
loop.QuitClosure(), &result, agent_cluster_id_));
loop.Run();
return result;
}
const std::string kId = "id";
const url::Origin kOrigin = url::Origin::Create(GURL("https://example.com"));
const GURL kValidUrl = GURL("blob:" + kOrigin.Serialize() + "/id1");
const GURL kValidUrl2 = GURL("blob:" + kOrigin.Serialize() + "/id2");
const GURL kInvalidUrl = GURL("bolb:id");
const GURL kFragmentUrl = GURL(kValidUrl.spec() + "#fragment");
const url::Origin kWrongOrigin =
url::Origin::Create(GURL("https://test.com"));
const GURL kWrongOriginUrl = GURL("blob:" + kWrongOrigin.Serialize() + "/id");
protected:
base::test::TaskEnvironment task_environment_;
std::unique_ptr<BlobStorageContext> context_;
BlobUrlRegistry url_registry_;
std::vector<std::string> bad_messages_;
base::UnguessableToken agent_cluster_id_;
};
TEST_F(BlobURLStoreImplTest, BasicRegisterRevoke) {
mojo::PendingRemote<blink::mojom::Blob> blob =
CreateBlobFromString(kId, "hello world");
// Register a URL and make sure the URL keeps the blob alive.
BlobURLStoreImpl url_store(kOrigin, url_registry_.AsWeakPtr());
RegisterURL(&url_store, std::move(blob), kValidUrl);
blob = url_registry_.GetBlobFromUrl(kValidUrl);
ASSERT_TRUE(blob);
mojo::Remote<blink::mojom::Blob> blob_remote(std::move(blob));
EXPECT_EQ(kId, UUIDFromBlob(blob_remote.get()));
blob_remote.reset();
// Revoke the URL.
url_store.Revoke(kValidUrl);
blob = url_registry_.GetBlobFromUrl(kValidUrl);
EXPECT_FALSE(blob);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(context_->registry().HasEntry(kId));
}
TEST_F(BlobURLStoreImplTest, RegisterInvalidScheme) {
mojo::PendingRemote<blink::mojom::Blob> blob =
CreateBlobFromString(kId, "hello world");
mojo::Remote<BlobURLStore> url_store(CreateURLStore());
RegisterURL(url_store.get(), std::move(blob), kInvalidUrl);
EXPECT_FALSE(url_registry_.GetBlobFromUrl(kInvalidUrl));
EXPECT_EQ(1u, bad_messages_.size());
}
TEST_F(BlobURLStoreImplTest, RegisterWrongOrigin) {
mojo::PendingRemote<blink::mojom::Blob> blob =
CreateBlobFromString(kId, "hello world");
mojo::Remote<BlobURLStore> url_store(CreateURLStore());
RegisterURL(url_store.get(), std::move(blob), kWrongOriginUrl);
EXPECT_FALSE(url_registry_.GetBlobFromUrl(kWrongOriginUrl));
EXPECT_EQ(1u, bad_messages_.size());
}
TEST_F(BlobURLStoreImplTest, RegisterUrlFragment) {
mojo::PendingRemote<blink::mojom::Blob> blob =
CreateBlobFromString(kId, "hello world");
mojo::Remote<BlobURLStore> url_store(CreateURLStore());
RegisterURL(url_store.get(), std::move(blob), kFragmentUrl);
EXPECT_FALSE(url_registry_.GetBlobFromUrl(kFragmentUrl));
EXPECT_EQ(1u, bad_messages_.size());
}
TEST_F(BlobURLStoreImplTest, ImplicitRevoke) {
mojo::Remote<blink::mojom::Blob> blob(
CreateBlobFromString(kId, "hello world"));
mojo::PendingRemote<blink::mojom::Blob> blob2;
blob->Clone(blob2.InitWithNewPipeAndPassReceiver());
auto url_store =
std::make_unique<BlobURLStoreImpl>(kOrigin, url_registry_.AsWeakPtr());
RegisterURL(url_store.get(), blob.Unbind(), kValidUrl);
EXPECT_TRUE(url_registry_.GetBlobFromUrl(kValidUrl));
RegisterURL(url_store.get(), std::move(blob2), kValidUrl2);
EXPECT_TRUE(url_registry_.GetBlobFromUrl(kValidUrl2));
// Destroy URL Store, should revoke URLs.
url_store = nullptr;
EXPECT_FALSE(url_registry_.GetBlobFromUrl(kValidUrl));
EXPECT_FALSE(url_registry_.GetBlobFromUrl(kValidUrl2));
}
TEST_F(BlobURLStoreImplTest, RevokeThroughDifferentURLStore) {
mojo::PendingRemote<blink::mojom::Blob> blob =
CreateBlobFromString(kId, "hello world");
BlobURLStoreImpl url_store1(kOrigin, url_registry_.AsWeakPtr());
BlobURLStoreImpl url_store2(kOrigin, url_registry_.AsWeakPtr());
RegisterURL(&url_store1, std::move(blob), kValidUrl);
EXPECT_TRUE(url_registry_.GetBlobFromUrl(kValidUrl));
url_store2.Revoke(kValidUrl);
EXPECT_FALSE(url_registry_.GetBlobFromUrl(kValidUrl));
}
TEST_F(BlobURLStoreImplTest, RevokeInvalidScheme) {
mojo::Remote<BlobURLStore> url_store(CreateURLStore());
url_store->Revoke(kInvalidUrl);
url_store.FlushForTesting();
EXPECT_EQ(1u, bad_messages_.size());
}
TEST_F(BlobURLStoreImplTest, RevokeWrongOrigin) {
mojo::Remote<BlobURLStore> url_store(CreateURLStore());
url_store->Revoke(kWrongOriginUrl);
url_store.FlushForTesting();
EXPECT_EQ(1u, bad_messages_.size());
}
TEST_F(BlobURLStoreImplTest, RevokeURLWithFragment) {
mojo::Remote<BlobURLStore> url_store(CreateURLStore());
url_store->Revoke(kFragmentUrl);
url_store.FlushForTesting();
EXPECT_EQ(1u, bad_messages_.size());
}
TEST_F(BlobURLStoreImplTest, Resolve) {
mojo::PendingRemote<blink::mojom::Blob> blob =
CreateBlobFromString(kId, "hello world");
BlobURLStoreImpl url_store(kOrigin, url_registry_.AsWeakPtr());
RegisterURL(&url_store, std::move(blob), kValidUrl);
blob = ResolveURL(&url_store, kValidUrl);
ASSERT_TRUE(blob);
mojo::Remote<blink::mojom::Blob> blob_remote(std::move(blob));
EXPECT_EQ(kId, UUIDFromBlob(blob_remote.get()));
blob = ResolveURL(&url_store, kFragmentUrl);
ASSERT_TRUE(blob);
blob_remote.reset();
blob_remote.Bind(std::move(blob));
EXPECT_EQ(kId, UUIDFromBlob(blob_remote.get()));
}
TEST_F(BlobURLStoreImplTest, ResolveNonExistentURL) {
BlobURLStoreImpl url_store(kOrigin, url_registry_.AsWeakPtr());
mojo::PendingRemote<blink::mojom::Blob> blob =
ResolveURL(&url_store, kValidUrl);
EXPECT_FALSE(blob);
blob = ResolveURL(&url_store, kFragmentUrl);
EXPECT_FALSE(blob);
}
TEST_F(BlobURLStoreImplTest, ResolveInvalidURL) {
BlobURLStoreImpl url_store(kOrigin, url_registry_.AsWeakPtr());
mojo::PendingRemote<blink::mojom::Blob> blob =
ResolveURL(&url_store, kInvalidUrl);
EXPECT_FALSE(blob);
}
TEST_F(BlobURLStoreImplTest, ResolveAsURLLoaderFactory) {
mojo::PendingRemote<blink::mojom::Blob> blob =
CreateBlobFromString(kId, "hello world");
BlobURLStoreImpl url_store(kOrigin, url_registry_.AsWeakPtr());
RegisterURL(&url_store, std::move(blob), kValidUrl);
mojo::Remote<network::mojom::URLLoaderFactory> factory;
base::RunLoop resolve_loop;
url_store.ResolveAsURLLoaderFactory(
kValidUrl, factory.BindNewPipeAndPassReceiver(),
base::BindOnce(
[](base::OnceClosure done,
const base::UnguessableToken& agent_registered,
const absl::optional<base::UnguessableToken>&
unsafe_agent_cluster_id) {
EXPECT_EQ(agent_registered, unsafe_agent_cluster_id);
std::move(done).Run();
},
resolve_loop.QuitClosure(), agent_cluster_id_));
resolve_loop.Run();
auto request = std::make_unique<network::ResourceRequest>();
request->url = kValidUrl;
auto loader = network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
base::RunLoop download_loop;
loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
factory.get(), base::BindLambdaForTesting(
[&](std::unique_ptr<std::string> response_body) {
download_loop.Quit();
ASSERT_TRUE(response_body);
EXPECT_EQ("hello world", *response_body);
}));
download_loop.Run();
}
TEST_F(BlobURLStoreImplTest, ResolveForNavigation) {
mojo::PendingRemote<blink::mojom::Blob> blob =
CreateBlobFromString(kId, "hello world");
BlobURLStoreImpl url_store(kOrigin, url_registry_.AsWeakPtr());
RegisterURL(&url_store, std::move(blob), kValidUrl);
base::RunLoop loop0;
mojo::Remote<blink::mojom::BlobURLToken> token_remote;
url_store.ResolveForNavigation(
kValidUrl, token_remote.BindNewPipeAndPassReceiver(),
base::BindOnce(
[](base::OnceClosure done,
const base::UnguessableToken& agent_registered,
const absl::optional<base::UnguessableToken>&
unsafe_agent_cluster_id) {
EXPECT_EQ(agent_registered, unsafe_agent_cluster_id);
std::move(done).Run();
},
loop0.QuitClosure(), agent_cluster_id_));
loop0.Run();
base::UnguessableToken token;
base::RunLoop loop;
token_remote->GetToken(base::BindLambdaForTesting(
[&](const base::UnguessableToken& received_token) {
token = received_token;
loop.Quit();
}));
loop.Run();
GURL blob_url;
EXPECT_TRUE(url_registry_.GetTokenMapping(token, &blob_url, &blob));
EXPECT_EQ(kValidUrl, blob_url);
mojo::Remote<blink::mojom::Blob> blob_remote(std::move(blob));
EXPECT_EQ(kId, UUIDFromBlob(blob_remote.get()));
}
} // namespace storage