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