1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "storage/browser/blob/blob_url_store_impl.h"
6
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/test/bind.h"
10 #include "base/test/task_environment.h"
11 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
12 #include "mojo/public/cpp/system/functions.h"
13 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
14 #include "services/network/public/cpp/simple_url_loader.h"
15 #include "services/network/public/mojom/url_loader_factory.mojom.h"
16 #include "storage/browser/blob/blob_data_builder.h"
17 #include "storage/browser/blob/blob_impl.h"
18 #include "storage/browser/blob/blob_storage_context.h"
19 #include "storage/browser/blob/blob_url_registry.h"
20 #include "storage/browser/test/mock_blob_registry_delegate.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 using blink::mojom::BlobURLStore;
24
25 namespace storage {
26
27 class BlobURLStoreImplTest : public testing::Test {
28 public:
SetUp()29 void SetUp() override {
30 context_ = std::make_unique<BlobStorageContext>();
31
32 mojo::SetDefaultProcessErrorHandler(base::BindRepeating(
33 &BlobURLStoreImplTest::OnBadMessage, base::Unretained(this)));
34 }
35
TearDown()36 void TearDown() override {
37 mojo::SetDefaultProcessErrorHandler(base::NullCallback());
38 }
39
OnBadMessage(const std::string & error)40 void OnBadMessage(const std::string& error) {
41 bad_messages_.push_back(error);
42 }
43
CreateBlobFromString(const std::string & uuid,const std::string & contents)44 mojo::PendingRemote<blink::mojom::Blob> CreateBlobFromString(
45 const std::string& uuid,
46 const std::string& contents) {
47 auto builder = std::make_unique<BlobDataBuilder>(uuid);
48 builder->set_content_type("text/plain");
49 builder->AppendData(contents);
50 mojo::PendingRemote<blink::mojom::Blob> blob;
51 BlobImpl::Create(context_->AddFinishedBlob(std::move(builder)),
52 blob.InitWithNewPipeAndPassReceiver());
53 return blob;
54 }
55
UUIDFromBlob(blink::mojom::Blob * blob)56 std::string UUIDFromBlob(blink::mojom::Blob* blob) {
57 base::RunLoop loop;
58 std::string received_uuid;
59 blob->GetInternalUUID(base::BindOnce(
60 [](base::OnceClosure quit_closure, std::string* uuid_out,
61 const std::string& uuid) {
62 *uuid_out = uuid;
63 std::move(quit_closure).Run();
64 },
65 loop.QuitClosure(), &received_uuid));
66 loop.Run();
67 return received_uuid;
68 }
69
CreateURLStore()70 mojo::PendingRemote<BlobURLStore> CreateURLStore() {
71 mojo::PendingRemote<BlobURLStore> result;
72 mojo::MakeSelfOwnedReceiver(std::make_unique<BlobURLStoreImpl>(
73 url_registry_.AsWeakPtr(), &delegate_),
74 result.InitWithNewPipeAndPassReceiver());
75 return result;
76 }
77
RegisterURL(BlobURLStore * store,mojo::PendingRemote<blink::mojom::Blob> blob,const GURL & url)78 void RegisterURL(BlobURLStore* store,
79 mojo::PendingRemote<blink::mojom::Blob> blob,
80 const GURL& url) {
81 base::RunLoop loop;
82 store->Register(std::move(blob), url, loop.QuitClosure());
83 loop.Run();
84 }
85
ResolveURL(BlobURLStore * store,const GURL & url)86 mojo::PendingRemote<blink::mojom::Blob> ResolveURL(BlobURLStore* store,
87 const GURL& url) {
88 mojo::PendingRemote<blink::mojom::Blob> result;
89 base::RunLoop loop;
90 store->Resolve(kValidUrl,
91 base::BindOnce(
92 [](base::OnceClosure done,
93 mojo::PendingRemote<blink::mojom::Blob>* blob_out,
94 mojo::PendingRemote<blink::mojom::Blob> blob) {
95 *blob_out = std::move(blob);
96 std::move(done).Run();
97 },
98 loop.QuitClosure(), &result));
99 loop.Run();
100 return result;
101 }
102
103 const std::string kId = "id";
104 const GURL kValidUrl = GURL("blob:id");
105 const GURL kInvalidUrl = GURL("bolb:id");
106 const GURL kFragmentUrl = GURL("blob:id#fragment");
107
108 protected:
109 base::test::TaskEnvironment task_environment_;
110 std::unique_ptr<BlobStorageContext> context_;
111 BlobUrlRegistry url_registry_;
112 MockBlobRegistryDelegate delegate_;
113 std::vector<std::string> bad_messages_;
114 };
115
TEST_F(BlobURLStoreImplTest,BasicRegisterRevoke)116 TEST_F(BlobURLStoreImplTest, BasicRegisterRevoke) {
117 mojo::PendingRemote<blink::mojom::Blob> blob =
118 CreateBlobFromString(kId, "hello world");
119
120 // Register a URL and make sure the URL keeps the blob alive.
121 BlobURLStoreImpl url_store(url_registry_.AsWeakPtr(), &delegate_);
122 RegisterURL(&url_store, std::move(blob), kValidUrl);
123
124 blob = url_registry_.GetBlobFromUrl(kValidUrl);
125 ASSERT_TRUE(blob);
126 mojo::Remote<blink::mojom::Blob> blob_remote(std::move(blob));
127 EXPECT_EQ(kId, UUIDFromBlob(blob_remote.get()));
128 blob_remote.reset();
129
130 // Revoke the URL.
131 url_store.Revoke(kValidUrl);
132 blob = url_registry_.GetBlobFromUrl(kValidUrl);
133 EXPECT_FALSE(blob);
134
135 base::RunLoop().RunUntilIdle();
136 EXPECT_FALSE(context_->registry().HasEntry(kId));
137 }
138
TEST_F(BlobURLStoreImplTest,RegisterInvalidScheme)139 TEST_F(BlobURLStoreImplTest, RegisterInvalidScheme) {
140 mojo::PendingRemote<blink::mojom::Blob> blob =
141 CreateBlobFromString(kId, "hello world");
142
143 mojo::Remote<BlobURLStore> url_store(CreateURLStore());
144 RegisterURL(url_store.get(), std::move(blob), kInvalidUrl);
145 EXPECT_FALSE(url_registry_.GetBlobFromUrl(kInvalidUrl));
146 EXPECT_EQ(1u, bad_messages_.size());
147 }
148
TEST_F(BlobURLStoreImplTest,RegisterCantCommit)149 TEST_F(BlobURLStoreImplTest, RegisterCantCommit) {
150 mojo::PendingRemote<blink::mojom::Blob> blob =
151 CreateBlobFromString(kId, "hello world");
152
153 delegate_.can_commit_url_result = false;
154
155 mojo::Remote<BlobURLStore> url_store(CreateURLStore());
156 RegisterURL(url_store.get(), std::move(blob), kValidUrl);
157 EXPECT_FALSE(url_registry_.GetBlobFromUrl(kValidUrl));
158 EXPECT_EQ(1u, bad_messages_.size());
159 }
160
TEST_F(BlobURLStoreImplTest,RegisterUrlFragment)161 TEST_F(BlobURLStoreImplTest, RegisterUrlFragment) {
162 mojo::PendingRemote<blink::mojom::Blob> blob =
163 CreateBlobFromString(kId, "hello world");
164
165 mojo::Remote<BlobURLStore> url_store(CreateURLStore());
166 RegisterURL(url_store.get(), std::move(blob), kFragmentUrl);
167 EXPECT_FALSE(url_registry_.GetBlobFromUrl(kFragmentUrl));
168 EXPECT_EQ(1u, bad_messages_.size());
169 }
170
TEST_F(BlobURLStoreImplTest,ImplicitRevoke)171 TEST_F(BlobURLStoreImplTest, ImplicitRevoke) {
172 const GURL kValidUrl2("blob:id2");
173 mojo::Remote<blink::mojom::Blob> blob(
174 CreateBlobFromString(kId, "hello world"));
175 mojo::PendingRemote<blink::mojom::Blob> blob2;
176 blob->Clone(blob2.InitWithNewPipeAndPassReceiver());
177
178 auto url_store =
179 std::make_unique<BlobURLStoreImpl>(url_registry_.AsWeakPtr(), &delegate_);
180 RegisterURL(url_store.get(), blob.Unbind(), kValidUrl);
181 EXPECT_TRUE(url_registry_.GetBlobFromUrl(kValidUrl));
182 RegisterURL(url_store.get(), std::move(blob2), kValidUrl2);
183 EXPECT_TRUE(url_registry_.GetBlobFromUrl(kValidUrl2));
184
185 // Destroy URL Store, should revoke URLs.
186 url_store = nullptr;
187 EXPECT_FALSE(url_registry_.GetBlobFromUrl(kValidUrl));
188 EXPECT_FALSE(url_registry_.GetBlobFromUrl(kValidUrl2));
189 }
190
TEST_F(BlobURLStoreImplTest,RevokeThroughDifferentURLStore)191 TEST_F(BlobURLStoreImplTest, RevokeThroughDifferentURLStore) {
192 mojo::PendingRemote<blink::mojom::Blob> blob =
193 CreateBlobFromString(kId, "hello world");
194
195 BlobURLStoreImpl url_store1(url_registry_.AsWeakPtr(), &delegate_);
196 BlobURLStoreImpl url_store2(url_registry_.AsWeakPtr(), &delegate_);
197
198 RegisterURL(&url_store1, std::move(blob), kValidUrl);
199 EXPECT_TRUE(url_registry_.GetBlobFromUrl(kValidUrl));
200
201 url_store2.Revoke(kValidUrl);
202 EXPECT_FALSE(url_registry_.GetBlobFromUrl(kValidUrl));
203 }
204
TEST_F(BlobURLStoreImplTest,RevokeInvalidScheme)205 TEST_F(BlobURLStoreImplTest, RevokeInvalidScheme) {
206 mojo::Remote<BlobURLStore> url_store(CreateURLStore());
207 url_store->Revoke(kInvalidUrl);
208 url_store.FlushForTesting();
209 EXPECT_EQ(1u, bad_messages_.size());
210 }
211
TEST_F(BlobURLStoreImplTest,RevokeCantCommit)212 TEST_F(BlobURLStoreImplTest, RevokeCantCommit) {
213 delegate_.can_commit_url_result = false;
214
215 mojo::Remote<BlobURLStore> url_store(CreateURLStore());
216 url_store->Revoke(kValidUrl);
217 url_store.FlushForTesting();
218 EXPECT_EQ(1u, bad_messages_.size());
219 }
220
TEST_F(BlobURLStoreImplTest,RevokeURLWithFragment)221 TEST_F(BlobURLStoreImplTest, RevokeURLWithFragment) {
222 mojo::Remote<BlobURLStore> url_store(CreateURLStore());
223 url_store->Revoke(kFragmentUrl);
224 url_store.FlushForTesting();
225 EXPECT_EQ(1u, bad_messages_.size());
226 }
227
TEST_F(BlobURLStoreImplTest,Resolve)228 TEST_F(BlobURLStoreImplTest, Resolve) {
229 mojo::PendingRemote<blink::mojom::Blob> blob =
230 CreateBlobFromString(kId, "hello world");
231
232 BlobURLStoreImpl url_store(url_registry_.AsWeakPtr(), &delegate_);
233 RegisterURL(&url_store, std::move(blob), kValidUrl);
234
235 blob = ResolveURL(&url_store, kValidUrl);
236 ASSERT_TRUE(blob);
237 mojo::Remote<blink::mojom::Blob> blob_remote(std::move(blob));
238 EXPECT_EQ(kId, UUIDFromBlob(blob_remote.get()));
239 blob = ResolveURL(&url_store, kFragmentUrl);
240 ASSERT_TRUE(blob);
241 blob_remote.reset();
242 blob_remote.Bind(std::move(blob));
243 EXPECT_EQ(kId, UUIDFromBlob(blob_remote.get()));
244 }
245
TEST_F(BlobURLStoreImplTest,ResolveNonExistentURL)246 TEST_F(BlobURLStoreImplTest, ResolveNonExistentURL) {
247 BlobURLStoreImpl url_store(url_registry_.AsWeakPtr(), &delegate_);
248
249 mojo::PendingRemote<blink::mojom::Blob> blob =
250 ResolveURL(&url_store, kValidUrl);
251 EXPECT_FALSE(blob);
252 blob = ResolveURL(&url_store, kFragmentUrl);
253 EXPECT_FALSE(blob);
254 }
255
TEST_F(BlobURLStoreImplTest,ResolveInvalidURL)256 TEST_F(BlobURLStoreImplTest, ResolveInvalidURL) {
257 BlobURLStoreImpl url_store(url_registry_.AsWeakPtr(), &delegate_);
258
259 mojo::PendingRemote<blink::mojom::Blob> blob =
260 ResolveURL(&url_store, kInvalidUrl);
261 EXPECT_FALSE(blob);
262 }
263
TEST_F(BlobURLStoreImplTest,ResolveAsURLLoaderFactory)264 TEST_F(BlobURLStoreImplTest, ResolveAsURLLoaderFactory) {
265 mojo::PendingRemote<blink::mojom::Blob> blob =
266 CreateBlobFromString(kId, "hello world");
267
268 BlobURLStoreImpl url_store(url_registry_.AsWeakPtr(), &delegate_);
269 RegisterURL(&url_store, std::move(blob), kValidUrl);
270
271 mojo::Remote<network::mojom::URLLoaderFactory> factory;
272 url_store.ResolveAsURLLoaderFactory(kValidUrl,
273 factory.BindNewPipeAndPassReceiver());
274
275 auto request = std::make_unique<network::ResourceRequest>();
276 request->url = kValidUrl;
277 auto loader = network::SimpleURLLoader::Create(std::move(request),
278 TRAFFIC_ANNOTATION_FOR_TESTS);
279 base::RunLoop loop;
280 loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
281 factory.get(), base::BindLambdaForTesting(
282 [&](std::unique_ptr<std::string> response_body) {
283 loop.Quit();
284 ASSERT_TRUE(response_body);
285 EXPECT_EQ("hello world", *response_body);
286 }));
287 loop.Run();
288 }
289
TEST_F(BlobURLStoreImplTest,ResolveForNavigation)290 TEST_F(BlobURLStoreImplTest, ResolveForNavigation) {
291 mojo::PendingRemote<blink::mojom::Blob> blob =
292 CreateBlobFromString(kId, "hello world");
293
294 BlobURLStoreImpl url_store(url_registry_.AsWeakPtr(), &delegate_);
295 RegisterURL(&url_store, std::move(blob), kValidUrl);
296
297 mojo::Remote<blink::mojom::BlobURLToken> token_remote;
298 url_store.ResolveForNavigation(kValidUrl,
299 token_remote.BindNewPipeAndPassReceiver());
300
301 base::UnguessableToken token;
302 base::RunLoop loop;
303 token_remote->GetToken(base::BindLambdaForTesting(
304 [&](const base::UnguessableToken& received_token) {
305 token = received_token;
306 loop.Quit();
307 }));
308 loop.Run();
309
310 GURL blob_url;
311 EXPECT_TRUE(url_registry_.GetTokenMapping(token, &blob_url, &blob));
312 EXPECT_EQ(kValidUrl, blob_url);
313 mojo::Remote<blink::mojom::Blob> blob_remote(std::move(blob));
314 EXPECT_EQ(kId, UUIDFromBlob(blob_remote.get()));
315 }
316
317 } // namespace storage
318