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