1 // Copyright 2018 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 "third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h"
6
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "third_party/blink/public/platform/web_crypto.h"
9 #include "third_party/blink/renderer/platform/crypto.h"
10 #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
11 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
12
13 namespace blink {
14
15 namespace {
16
17 // Structure holding cache metadata sent to the platform.
18 struct CacheMetadataEntry {
CacheMetadataEntryblink::__anon9c8578bf0111::CacheMetadataEntry19 CacheMetadataEntry(const WebURL& url,
20 base::Time response_time,
21 const uint8_t* data,
22 wtf_size_t data_size)
23 : url(url), response_time(response_time) {
24 this->data.Append(data, data_size);
25 }
26
27 WebURL url;
28 base::Time response_time;
29 Vector<uint8_t> data;
30 };
31
32 // Mock Platform implementation that provides basic crypto and caching.
33 class SourceKeyedCachedMetadataHandlerMockPlatform final
34 : public TestingPlatformSupportWithMockScheduler {
35 public:
SourceKeyedCachedMetadataHandlerMockPlatform()36 SourceKeyedCachedMetadataHandlerMockPlatform() {}
37 ~SourceKeyedCachedMetadataHandlerMockPlatform() override = default;
38
CacheMetadata(blink::mojom::CodeCacheType cache_type,const WebURL & url,base::Time response_time,const uint8_t * data,size_t data_size)39 void CacheMetadata(blink::mojom::CodeCacheType cache_type,
40 const WebURL& url,
41 base::Time response_time,
42 const uint8_t* data,
43 size_t data_size) override {
44 cache_entries_.emplace_back(url, response_time, data,
45 SafeCast<wtf_size_t>(data_size));
46 }
47
HasCacheMetadataFor(const WebURL & url)48 bool HasCacheMetadataFor(const WebURL& url) {
49 for (const CacheMetadataEntry& entry : cache_entries_) {
50 if (entry.url == url) {
51 return true;
52 }
53 }
54 return false;
55 }
56
GetCacheMetadatasFor(const WebURL & url)57 Vector<CacheMetadataEntry> GetCacheMetadatasFor(const WebURL& url) {
58 Vector<CacheMetadataEntry> url_entries;
59 for (const CacheMetadataEntry& entry : cache_entries_) {
60 if (entry.url == url) {
61 url_entries.push_back(entry);
62 }
63 }
64 return url_entries;
65 }
66
67 private:
68 Vector<CacheMetadataEntry> cache_entries_;
69 };
70
71 // Mock CachedMetadataSender implementation that forwards data to the platform.
72 class MockCachedMetadataSender final : public CachedMetadataSender {
73 public:
MockCachedMetadataSender(KURL response_url)74 MockCachedMetadataSender(KURL response_url) : response_url_(response_url) {}
75
Send(const uint8_t * data,size_t size)76 void Send(const uint8_t* data, size_t size) override {
77 Platform::Current()->CacheMetadata(blink::mojom::CodeCacheType::kJavascript,
78 response_url_, response_time_, data,
79 size);
80 }
81
IsServedFromCacheStorage()82 bool IsServedFromCacheStorage() override { return false; }
83
84 private:
85 const KURL response_url_;
86 const base::Time response_time_;
87 };
88
CachedMetadataFailure(const char * failure_msg,const char * actual_expression,const Vector<uint8_t> & expected,const scoped_refptr<CachedMetadata> & actual)89 ::testing::AssertionResult CachedMetadataFailure(
90 const char* failure_msg,
91 const char* actual_expression,
92 const Vector<uint8_t>& expected,
93 const scoped_refptr<CachedMetadata>& actual) {
94 ::testing::Message msg;
95 msg << failure_msg << " for " << actual_expression;
96 msg << "\n Expected: [" << expected.size() << "] { ";
97 for (size_t i = 0; i < expected.size(); ++i) {
98 if (i > 0)
99 msg << ", ";
100 msg << std::hex << static_cast<int>(expected[i]);
101 }
102 msg << " }";
103 if (actual) {
104 msg << "\n Actual: [" << actual->size() << "] { ";
105 for (size_t i = 0; i < actual->size(); ++i) {
106 if (i > 0)
107 msg << ", ";
108 msg << std::hex << static_cast<int>(actual->Data()[i]);
109 }
110 msg << " }";
111 } else {
112 msg << "\n Actual: (null)";
113 }
114
115 return testing::AssertionFailure() << msg;
116 }
117
CachedMetadataEqual(const char * expected_expression,const char * actual_expression,const Vector<uint8_t> & expected,const scoped_refptr<CachedMetadata> & actual)118 ::testing::AssertionResult CachedMetadataEqual(
119 const char* expected_expression,
120 const char* actual_expression,
121 const Vector<uint8_t>& expected,
122 const scoped_refptr<CachedMetadata>& actual) {
123 if (!actual) {
124 return CachedMetadataFailure("Expected non-null data", actual_expression,
125 expected, actual);
126 }
127 if (actual->size() != expected.size()) {
128 return CachedMetadataFailure("Wrong size", actual_expression, expected,
129 actual);
130 }
131 const uint8_t* actual_data = actual->Data();
132 for (size_t i = 0; i < expected.size(); ++i) {
133 if (actual_data[i] != expected[i]) {
134 return CachedMetadataFailure("Wrong data", actual_expression, expected,
135 actual);
136 }
137 }
138
139 return testing::AssertionSuccess();
140 }
141
142 #define EXPECT_METADATA(data_array, cached_metadata) \
143 EXPECT_PRED_FORMAT2(CachedMetadataEqual, data_array, cached_metadata)
144
145 } // namespace
146
TEST(SourceKeyedCachedMetadataHandlerTest,HandlerForSource_InitiallyNonNullHandlersWithNullData)147 TEST(SourceKeyedCachedMetadataHandlerTest,
148 HandlerForSource_InitiallyNonNullHandlersWithNullData) {
149 ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
150 platform;
151
152 KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
153 SourceKeyedCachedMetadataHandler* handler =
154 MakeGarbageCollected<SourceKeyedCachedMetadataHandler>(
155 WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
156
157 WTF::String source1("source1");
158 SingleCachedMetadataHandler* source1_handler =
159 handler->HandlerForSource(source1);
160
161 WTF::String source2("source2");
162 SingleCachedMetadataHandler* source2_handler =
163 handler->HandlerForSource(source2);
164
165 EXPECT_NE(nullptr, source1_handler);
166 EXPECT_EQ(nullptr, source1_handler->GetCachedMetadata(0xbeef));
167 EXPECT_NE(nullptr, source2_handler);
168 EXPECT_EQ(nullptr, source2_handler->GetCachedMetadata(0x5eed));
169 }
170
TEST(SourceKeyedCachedMetadataHandlerTest,HandlerForSource_OneHandlerSetOtherNull)171 TEST(SourceKeyedCachedMetadataHandlerTest,
172 HandlerForSource_OneHandlerSetOtherNull) {
173 ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
174 platform;
175
176 KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
177 SourceKeyedCachedMetadataHandler* handler =
178 MakeGarbageCollected<SourceKeyedCachedMetadataHandler>(
179 WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
180
181 WTF::String source1("source1");
182 SingleCachedMetadataHandler* source1_handler =
183 handler->HandlerForSource(source1);
184
185 WTF::String source2("source2");
186 SingleCachedMetadataHandler* source2_handler =
187 handler->HandlerForSource(source2);
188
189 Vector<uint8_t> data1 = {1, 2, 3};
190 source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size());
191
192 EXPECT_NE(nullptr, source1_handler);
193 EXPECT_METADATA(data1, source1_handler->GetCachedMetadata(0xbeef));
194
195 EXPECT_NE(nullptr, source2_handler);
196 EXPECT_EQ(nullptr, source2_handler->GetCachedMetadata(0x5eed));
197 }
198
TEST(SourceKeyedCachedMetadataHandlerTest,HandlerForSource_BothHandlersSet)199 TEST(SourceKeyedCachedMetadataHandlerTest, HandlerForSource_BothHandlersSet) {
200 ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
201 platform;
202
203 KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
204 SourceKeyedCachedMetadataHandler* handler =
205 MakeGarbageCollected<SourceKeyedCachedMetadataHandler>(
206 WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
207
208 WTF::String source1("source1");
209 SingleCachedMetadataHandler* source1_handler =
210 handler->HandlerForSource(source1);
211
212 WTF::String source2("source2");
213 SingleCachedMetadataHandler* source2_handler =
214 handler->HandlerForSource(source2);
215
216 Vector<uint8_t> data1 = {1, 2, 3};
217 source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size());
218
219 Vector<uint8_t> data2 = {3, 4, 5, 6};
220 source2_handler->SetCachedMetadata(0x5eed, data2.data(), data2.size());
221
222 EXPECT_NE(nullptr, source1_handler);
223 EXPECT_METADATA(data1, source1_handler->GetCachedMetadata(0xbeef));
224
225 EXPECT_NE(nullptr, source2_handler);
226 EXPECT_METADATA(data2, source2_handler->GetCachedMetadata(0x5eed));
227 }
228
TEST(SourceKeyedCachedMetadataHandlerTest,Serialize_EmptyClearDoesSend)229 TEST(SourceKeyedCachedMetadataHandlerTest, Serialize_EmptyClearDoesSend) {
230 ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
231 platform;
232
233 KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
234 SourceKeyedCachedMetadataHandler* handler =
235 MakeGarbageCollected<SourceKeyedCachedMetadataHandler>(
236 WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
237
238 // Clear and send to the platform
239 handler->ClearCachedMetadata(CachedMetadataHandler::kClearPersistentStorage);
240
241 // Load from platform
242 Vector<CacheMetadataEntry> cache_metadatas =
243 platform->GetCacheMetadatasFor(url);
244
245 EXPECT_EQ(1u, cache_metadatas.size());
246 }
247
TEST(SourceKeyedCachedMetadataHandlerTest,Serialize_EachSetDoesSend)248 TEST(SourceKeyedCachedMetadataHandlerTest, Serialize_EachSetDoesSend) {
249 ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
250 platform;
251
252 KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
253 SourceKeyedCachedMetadataHandler* handler =
254 MakeGarbageCollected<SourceKeyedCachedMetadataHandler>(
255 WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
256
257 WTF::String source1("source1");
258 SingleCachedMetadataHandler* source1_handler =
259 handler->HandlerForSource(source1);
260
261 WTF::String source2("source2");
262 SingleCachedMetadataHandler* source2_handler =
263 handler->HandlerForSource(source2);
264
265 Vector<uint8_t> data1 = {1, 2, 3};
266 source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size());
267
268 Vector<uint8_t> data2 = {3, 4, 5, 6};
269 source2_handler->SetCachedMetadata(0x5eed, data2.data(), data2.size());
270
271 // Load from platform
272 Vector<CacheMetadataEntry> cache_metadatas =
273 platform->GetCacheMetadatasFor(url);
274
275 EXPECT_EQ(2u, cache_metadatas.size());
276 }
277
TEST(SourceKeyedCachedMetadataHandlerTest,Serialize_SetWithNoSendDoesNotSend)278 TEST(SourceKeyedCachedMetadataHandlerTest, Serialize_SetWithNoSendDoesNotSend) {
279 ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
280 platform;
281
282 KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
283 SourceKeyedCachedMetadataHandler* handler =
284 MakeGarbageCollected<SourceKeyedCachedMetadataHandler>(
285 WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
286
287 WTF::String source1("source1");
288 SingleCachedMetadataHandler* source1_handler =
289 handler->HandlerForSource(source1);
290
291 WTF::String source2("source2");
292 SingleCachedMetadataHandler* source2_handler =
293 handler->HandlerForSource(source2);
294
295 Vector<uint8_t> data1 = {1, 2, 3};
296 source1_handler->DisableSendToPlatformForTesting();
297 source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size());
298
299 Vector<uint8_t> data2 = {3, 4, 5, 6};
300 source2_handler->SetCachedMetadata(0x5eed, data2.data(), data2.size());
301
302 // Load from platform
303 Vector<CacheMetadataEntry> cache_metadatas =
304 platform->GetCacheMetadatasFor(url);
305
306 EXPECT_EQ(1u, cache_metadatas.size());
307 }
308
TEST(SourceKeyedCachedMetadataHandlerTest,SerializeAndDeserialize_NoHandlersSet)309 TEST(SourceKeyedCachedMetadataHandlerTest,
310 SerializeAndDeserialize_NoHandlersSet) {
311 ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
312 platform;
313
314 KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
315 WTF::String source1("source1");
316 WTF::String source2("source2");
317 {
318 SourceKeyedCachedMetadataHandler* handler =
319 MakeGarbageCollected<SourceKeyedCachedMetadataHandler>(
320 WTF::TextEncoding(),
321 std::make_unique<MockCachedMetadataSender>(url));
322
323 // Clear and persist in the platform.
324 handler->ClearCachedMetadata(
325 CachedMetadataHandler::kClearPersistentStorage);
326 }
327
328 // Reload from platform
329 {
330 Vector<CacheMetadataEntry> cache_metadatas =
331 platform->GetCacheMetadatasFor(url);
332 // Use the last data received by the platform
333 EXPECT_EQ(1u, cache_metadatas.size());
334 CacheMetadataEntry& last_cache_metadata = cache_metadatas[0];
335
336 SourceKeyedCachedMetadataHandler* handler =
337 MakeGarbageCollected<SourceKeyedCachedMetadataHandler>(
338 WTF::TextEncoding(),
339 std::make_unique<MockCachedMetadataSender>(url));
340 auto data = base::make_span(last_cache_metadata.data.data(),
341 last_cache_metadata.data.size());
342 handler->SetSerializedCachedMetadata(mojo_base::BigBuffer(data));
343
344 SingleCachedMetadataHandler* source1_handler =
345 handler->HandlerForSource(source1);
346 SingleCachedMetadataHandler* source2_handler =
347 handler->HandlerForSource(source2);
348
349 EXPECT_NE(nullptr, source1_handler);
350 EXPECT_EQ(nullptr, source1_handler->GetCachedMetadata(0xbeef));
351
352 EXPECT_NE(nullptr, source2_handler);
353 EXPECT_EQ(nullptr, source2_handler->GetCachedMetadata(0x5eed));
354 }
355 }
356
TEST(SourceKeyedCachedMetadataHandlerTest,SerializeAndDeserialize_BothHandlersSet)357 TEST(SourceKeyedCachedMetadataHandlerTest,
358 SerializeAndDeserialize_BothHandlersSet) {
359 ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
360 platform;
361
362 KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
363 WTF::String source1("source1");
364 WTF::String source2("source2");
365 Vector<uint8_t> data1 = {1, 2, 3};
366 Vector<uint8_t> data2 = {3, 4, 5, 6};
367 {
368 SourceKeyedCachedMetadataHandler* handler =
369 MakeGarbageCollected<SourceKeyedCachedMetadataHandler>(
370 WTF::TextEncoding(),
371 std::make_unique<MockCachedMetadataSender>(url));
372
373 SingleCachedMetadataHandler* source1_handler =
374 handler->HandlerForSource(source1);
375 SingleCachedMetadataHandler* source2_handler =
376 handler->HandlerForSource(source2);
377
378 source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size());
379 source2_handler->SetCachedMetadata(0x5eed, data2.data(), data2.size());
380 }
381
382 // Reload from platform
383 {
384 Vector<CacheMetadataEntry> cache_metadatas =
385 platform->GetCacheMetadatasFor(url);
386 // Use the last data received by the platform
387 EXPECT_EQ(2u, cache_metadatas.size());
388 CacheMetadataEntry& last_cache_metadata = cache_metadatas[1];
389
390 SourceKeyedCachedMetadataHandler* handler =
391 MakeGarbageCollected<SourceKeyedCachedMetadataHandler>(
392 WTF::TextEncoding(),
393 std::make_unique<MockCachedMetadataSender>(url));
394 auto data = base::make_span(last_cache_metadata.data.data(),
395 last_cache_metadata.data.size());
396 handler->SetSerializedCachedMetadata(mojo_base::BigBuffer(data));
397
398 SingleCachedMetadataHandler* source1_handler =
399 handler->HandlerForSource(source1);
400 SingleCachedMetadataHandler* source2_handler =
401 handler->HandlerForSource(source2);
402
403 EXPECT_NE(nullptr, source1_handler);
404 EXPECT_METADATA(data1, source1_handler->GetCachedMetadata(0xbeef));
405
406 EXPECT_NE(nullptr, source2_handler);
407 EXPECT_METADATA(data2, source2_handler->GetCachedMetadata(0x5eed));
408 }
409 }
410
411 } // namespace blink
412