1 // Copyright 2020 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 "content/browser/service_worker/service_worker_storage_control_impl.h"
6
7 #include "base/containers/span.h"
8 #include "base/files/scoped_temp_dir.h"
9 #include "base/test/bind_test_util.h"
10 #include "base/threading/thread_task_runner_handle.h"
11 #include "content/browser/service_worker/service_worker_storage.h"
12 #include "content/public/test/browser_task_environment.h"
13 #include "content/public/test/test_utils.h"
14 #include "net/disk_cache/disk_cache.h"
15 #include "net/http/http_util.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "third_party/blink/public/mojom/service_worker/navigation_preload_state.mojom.h"
18
19 namespace content {
20
21 using DatabaseStatus = storage::mojom::ServiceWorkerDatabaseStatus;
22 using FindRegistrationResult =
23 storage::mojom::ServiceWorkerFindRegistrationResultPtr;
24
25 namespace {
26
WriteResponseHead(storage::mojom::ServiceWorkerResourceWriter * writer,network::mojom::URLResponseHeadPtr response_head)27 int WriteResponseHead(storage::mojom::ServiceWorkerResourceWriter* writer,
28 network::mojom::URLResponseHeadPtr response_head) {
29 int return_value;
30 base::RunLoop loop;
31 writer->WriteResponseHead(std::move(response_head),
32 base::BindLambdaForTesting([&](int result) {
33 return_value = result;
34 loop.Quit();
35 }));
36 loop.Run();
37 return return_value;
38 }
39
WriteResponseData(storage::mojom::ServiceWorkerResourceWriter * writer,mojo_base::BigBuffer data)40 int WriteResponseData(storage::mojom::ServiceWorkerResourceWriter* writer,
41 mojo_base::BigBuffer data) {
42 int return_value;
43 base::RunLoop loop;
44 writer->WriteData(std::move(data),
45 base::BindLambdaForTesting([&](int result) {
46 return_value = result;
47 loop.Quit();
48 }));
49 loop.Run();
50 return return_value;
51 }
52
53 } // namespace
54
55 class ServiceWorkerStorageControlImplTest : public testing::Test {
56 public:
ServiceWorkerStorageControlImplTest()57 ServiceWorkerStorageControlImplTest()
58 : task_environment_(BrowserTaskEnvironment::IO_MAINLOOP) {}
59
SetUp()60 void SetUp() override {
61 ASSERT_TRUE(user_data_directory_.CreateUniqueTempDir());
62
63 auto storage = ServiceWorkerStorage::Create(
64 user_data_directory_.GetPath(),
65 /*database_task_runner=*/base::ThreadTaskRunnerHandle::Get(),
66 /*quota_manager_proxy=*/nullptr);
67 storage_impl_ =
68 std::make_unique<ServiceWorkerStorageControlImpl>(std::move(storage));
69 }
70
TearDown()71 void TearDown() override {
72 storage_impl_.reset();
73 disk_cache::FlushCacheThreadForTesting();
74 content::RunAllTasksUntilIdle();
75 }
76
storage()77 storage::mojom::ServiceWorkerStorageControl* storage() {
78 return storage_impl_.get();
79 }
80
LazyInitializeForTest()81 void LazyInitializeForTest() { storage_impl_->LazyInitializeForTest(); }
82
FindRegistrationForClientUrl(const GURL & client_url)83 FindRegistrationResult FindRegistrationForClientUrl(const GURL& client_url) {
84 FindRegistrationResult return_value;
85 base::RunLoop loop;
86 storage()->FindRegistrationForClientUrl(
87 client_url,
88 base::BindLambdaForTesting([&](FindRegistrationResult result) {
89 return_value = result.Clone();
90 loop.Quit();
91 }));
92 loop.Run();
93 return return_value;
94 }
95
FindRegistrationForScope(const GURL & scope)96 FindRegistrationResult FindRegistrationForScope(const GURL& scope) {
97 FindRegistrationResult return_value;
98 base::RunLoop loop;
99 storage()->FindRegistrationForScope(
100 scope, base::BindLambdaForTesting([&](FindRegistrationResult result) {
101 return_value = result.Clone();
102 loop.Quit();
103 }));
104 loop.Run();
105 return return_value;
106 }
107
FindRegistrationForId(int64_t registration_id,const GURL & origin)108 FindRegistrationResult FindRegistrationForId(int64_t registration_id,
109 const GURL& origin) {
110 FindRegistrationResult return_value;
111 base::RunLoop loop;
112 storage()->FindRegistrationForId(
113 registration_id, origin,
114 base::BindLambdaForTesting([&](FindRegistrationResult result) {
115 return_value = result.Clone();
116 loop.Quit();
117 }));
118 loop.Run();
119 return return_value;
120 }
121
StoreRegistration(storage::mojom::ServiceWorkerRegistrationDataPtr registration,std::vector<storage::mojom::ServiceWorkerResourceRecordPtr> resources,DatabaseStatus & out_status)122 void StoreRegistration(
123 storage::mojom::ServiceWorkerRegistrationDataPtr registration,
124 std::vector<storage::mojom::ServiceWorkerResourceRecordPtr> resources,
125 DatabaseStatus& out_status) {
126 base::RunLoop loop;
127 storage()->StoreRegistration(
128 std::move(registration), std::move(resources),
129 base::BindLambdaForTesting([&](DatabaseStatus status) {
130 out_status = status;
131 loop.Quit();
132 }));
133 loop.Run();
134 }
135
DeleteRegistration(int64_t registration_id,const GURL & origin,DatabaseStatus & out_status,storage::mojom::ServiceWorkerStorageOriginState & out_origin_state)136 void DeleteRegistration(
137 int64_t registration_id,
138 const GURL& origin,
139 DatabaseStatus& out_status,
140 storage::mojom::ServiceWorkerStorageOriginState& out_origin_state) {
141 base::RunLoop loop;
142 storage()->DeleteRegistration(
143 registration_id, origin,
144 base::BindLambdaForTesting(
145 [&](DatabaseStatus status,
146 storage::mojom::ServiceWorkerStorageOriginState origin_state) {
147 out_status = status;
148 out_origin_state = origin_state;
149 loop.Quit();
150 }));
151 loop.Run();
152 }
153
GetNewResourceId()154 int64_t GetNewResourceId() {
155 int64_t return_value;
156 base::RunLoop loop;
157 storage()->GetNewResourceId(
158 base::BindLambdaForTesting([&](int64_t resource_id) {
159 return_value = resource_id;
160 loop.Quit();
161 }));
162 loop.Run();
163 return return_value;
164 }
165
166 mojo::Remote<storage::mojom::ServiceWorkerResourceWriter>
CreateNewResourceWriter()167 CreateNewResourceWriter() {
168 mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer;
169 storage()->CreateResourceWriter(GetNewResourceId(),
170 writer.BindNewPipeAndPassReceiver());
171 return writer;
172 }
173
174 private:
175 base::ScopedTempDir user_data_directory_;
176 BrowserTaskEnvironment task_environment_;
177 std::unique_ptr<ServiceWorkerStorageControlImpl> storage_impl_;
178 };
179
180 // Tests that FindRegistration methods don't find anything without having stored
181 // anything.
TEST_F(ServiceWorkerStorageControlImplTest,FindRegistration_NoRegistration)182 TEST_F(ServiceWorkerStorageControlImplTest, FindRegistration_NoRegistration) {
183 const GURL kScope("https://www.example.com/scope/");
184 const GURL kClientUrl("https://www.example.com/scope/document.html");
185 const int64_t kRegistrationId = 0;
186
187 LazyInitializeForTest();
188
189 {
190 FindRegistrationResult result = FindRegistrationForClientUrl(kClientUrl);
191 EXPECT_EQ(result->status, DatabaseStatus::kErrorNotFound);
192 }
193
194 {
195 FindRegistrationResult result = FindRegistrationForScope(kScope);
196 EXPECT_EQ(result->status, DatabaseStatus::kErrorNotFound);
197 }
198
199 {
200 FindRegistrationResult result =
201 FindRegistrationForId(kRegistrationId, kScope.GetOrigin());
202 EXPECT_EQ(result->status, DatabaseStatus::kErrorNotFound);
203 }
204 }
205
206 // Tests that storing/finding/deleting a registration work.
TEST_F(ServiceWorkerStorageControlImplTest,StoreAndDeleteRegistration)207 TEST_F(ServiceWorkerStorageControlImplTest, StoreAndDeleteRegistration) {
208 const GURL kScope("https://www.example.com/scope/");
209 const GURL kScriptUrl("https://www.example.com/scope/sw.js");
210 const GURL kClientUrl("https://www.example.com/scope/document.html");
211 const int64_t kRegistrationId = 0;
212 const int64_t kScriptSize = 10;
213
214 LazyInitializeForTest();
215
216 // Create a registration data with a single resource.
217 std::vector<storage::mojom::ServiceWorkerResourceRecordPtr> resources;
218 resources.push_back(storage::mojom::ServiceWorkerResourceRecord::New(
219 kRegistrationId, kScriptUrl, kScriptSize));
220
221 auto data = storage::mojom::ServiceWorkerRegistrationData::New();
222 data->registration_id = kRegistrationId;
223 data->scope = kScope;
224 data->script = kScriptUrl;
225 data->navigation_preload_state = blink::mojom::NavigationPreloadState::New();
226
227 int64_t resources_total_size_bytes = 0;
228 for (auto& resource : resources) {
229 resources_total_size_bytes += resource->size_bytes;
230 }
231 data->resources_total_size_bytes = resources_total_size_bytes;
232
233 // Store the registration data.
234 {
235 DatabaseStatus status;
236 StoreRegistration(std::move(data), std::move(resources), status);
237 ASSERT_EQ(status, DatabaseStatus::kOk);
238 }
239
240 // Find the registration. Find operations should succeed.
241 {
242 FindRegistrationResult result = FindRegistrationForClientUrl(kClientUrl);
243 ASSERT_EQ(result->status, DatabaseStatus::kOk);
244 EXPECT_EQ(result->registration->registration_id, kRegistrationId);
245 EXPECT_EQ(result->registration->scope, kScope);
246 EXPECT_EQ(result->registration->script, kScriptUrl);
247 EXPECT_EQ(result->registration->resources_total_size_bytes,
248 resources_total_size_bytes);
249 EXPECT_EQ(result->resources.size(), 1UL);
250
251 result = FindRegistrationForScope(kScope);
252 EXPECT_EQ(result->status, DatabaseStatus::kOk);
253 result = FindRegistrationForId(kRegistrationId, kScope.GetOrigin());
254 EXPECT_EQ(result->status, DatabaseStatus::kOk);
255 }
256
257 // Delete the registration.
258 {
259 DatabaseStatus status;
260 storage::mojom::ServiceWorkerStorageOriginState origin_state;
261 DeleteRegistration(kRegistrationId, kScope.GetOrigin(), status,
262 origin_state);
263 ASSERT_EQ(status, DatabaseStatus::kOk);
264 EXPECT_EQ(origin_state,
265 storage::mojom::ServiceWorkerStorageOriginState::kDelete);
266 }
267
268 // Try to find the deleted registration. These operation should result in
269 // kErrorNotFound.
270 {
271 FindRegistrationResult result = FindRegistrationForClientUrl(kClientUrl);
272 EXPECT_EQ(result->status, DatabaseStatus::kErrorNotFound);
273 result = FindRegistrationForScope(kScope);
274 EXPECT_EQ(result->status, DatabaseStatus::kErrorNotFound);
275 result = FindRegistrationForId(kRegistrationId, kScope.GetOrigin());
276 EXPECT_EQ(result->status, DatabaseStatus::kErrorNotFound);
277 }
278 }
279
280 // Tests that writing a service worker script succeeds.
TEST_F(ServiceWorkerStorageControlImplTest,WriteResource)281 TEST_F(ServiceWorkerStorageControlImplTest, WriteResource) {
282 LazyInitializeForTest();
283
284 mojo::Remote<storage::mojom::ServiceWorkerResourceWriter> writer =
285 CreateNewResourceWriter();
286
287 // Write a response head.
288 {
289 auto response_head = network::mojom::URLResponseHead::New();
290 response_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
291 net::HttpUtil::AssembleRawHeaders(
292 "HTTP/1.1 200 OK\n"
293 "Content-Type: application/javascript\n"));
294 response_head->headers->GetMimeType(&response_head->mime_type);
295
296 int result = WriteResponseHead(writer.get(), std::move(response_head));
297 ASSERT_GT(result, 0);
298 }
299
300 // Write content.
301 {
302 const std::string kData("/* script body */");
303 mojo_base::BigBuffer data(base::as_bytes(base::make_span(kData)));
304 int data_size = data.size();
305
306 int result = WriteResponseData(writer.get(), std::move(data));
307 ASSERT_EQ(data_size, result);
308 }
309
310 // TODO(crbug.com/1055677): Read the resource and check the response head and
311 // content.
312 }
313
314 } // namespace content
315