1 // Copyright 2015 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/core/html/parser/html_preload_scanner.h"
6 
7 #include <memory>
8 #include "base/strings/stringprintf.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "third_party/blink/public/platform/web_client_hints_type.h"
11 #include "third_party/blink/public/platform/web_runtime_features.h"
12 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
13 #include "third_party/blink/renderer/core/css/media_values_cached.h"
14 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
15 #include "third_party/blink/renderer/core/frame/settings.h"
16 #include "third_party/blink/renderer/core/html/cross_origin_attribute.h"
17 #include "third_party/blink/renderer/core/html/parser/html_parser_options.h"
18 #include "third_party/blink/renderer/core/html/parser/html_resource_preloader.h"
19 #include "third_party/blink/renderer/core/html/parser/preload_request.h"
20 #include "third_party/blink/renderer/core/media_type_names.h"
21 #include "third_party/blink/renderer/core/testing/page_test_base.h"
22 #include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
23 #include "third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h"
24 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
25 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
26 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
27 
28 namespace blink {
29 
30 struct PreloadScannerTestCase {
31   const char* base_url;
32   const char* input_html;
33   const char* preloaded_url;  // Or nullptr if no preload is expected.
34   const char* output_base_url;
35   ResourceType type;
36   int resource_width;
37   ClientHintsPreferences preferences;
38 };
39 
40 struct HTMLPreconnectTestCase {
41   const char* base_url;
42   const char* input_html;
43   const char* preconnected_host;
44   CrossOriginAttributeValue cross_origin;
45 };
46 
47 struct ReferrerPolicyTestCase {
48   const char* base_url;
49   const char* input_html;
50   const char* preloaded_url;  // Or nullptr if no preload is expected.
51   const char* output_base_url;
52   ResourceType type;
53   int resource_width;
54   network::mojom::ReferrerPolicy referrer_policy;
55   // Expected referrer header of the preload request, or nullptr if the header
56   // shouldn't be checked (and no network request should be created).
57   const char* expected_referrer;
58 };
59 
60 struct CorsTestCase {
61   const char* base_url;
62   const char* input_html;
63   network::mojom::RequestMode request_mode;
64   network::mojom::CredentialsMode credentials_mode;
65 };
66 
67 struct CSPTestCase {
68   const char* base_url;
69   const char* input_html;
70   bool should_see_csp_tag;
71 };
72 
73 struct NonceTestCase {
74   const char* base_url;
75   const char* input_html;
76   const char* nonce;
77 };
78 
79 struct ContextTestCase {
80   const char* base_url;
81   const char* input_html;
82   const char* preloaded_url;  // Or nullptr if no preload is expected.
83   bool is_image_set;
84 };
85 
86 struct IntegrityTestCase {
87   size_t number_of_integrity_metadata_found;
88   const char* input_html;
89 };
90 
91 struct LazyLoadImageTestCase {
92   const char* input_html;
93   bool lazy_load_image_enabled;
94 };
95 
96 class HTMLMockHTMLResourcePreloader : public ResourcePreloader {
97  public:
PreloadRequestVerification(ResourceType type,const char * url,const char * base_url,int width,const ClientHintsPreferences & preferences)98   void PreloadRequestVerification(ResourceType type,
99                                   const char* url,
100                                   const char* base_url,
101                                   int width,
102                                   const ClientHintsPreferences& preferences) {
103     if (!url) {
104       EXPECT_FALSE(preload_request_) << preload_request_->ResourceURL();
105       return;
106     }
107     EXPECT_NE(nullptr, preload_request_.get());
108     if (preload_request_) {
109       EXPECT_FALSE(preload_request_->IsPreconnect());
110       EXPECT_EQ(type, preload_request_->GetResourceType());
111       EXPECT_EQ(url, preload_request_->ResourceURL());
112       EXPECT_EQ(base_url, preload_request_->BaseURL().GetString());
113       EXPECT_EQ(width, preload_request_->ResourceWidth());
114       EXPECT_EQ(
115           preferences.ShouldSend(network::mojom::WebClientHintsType::kDpr),
116           preload_request_->Preferences().ShouldSend(
117               network::mojom::WebClientHintsType::kDpr));
118       EXPECT_EQ(preferences.ShouldSend(
119                     network::mojom::WebClientHintsType::kResourceWidth),
120                 preload_request_->Preferences().ShouldSend(
121                     network::mojom::WebClientHintsType::kResourceWidth));
122       EXPECT_EQ(preferences.ShouldSend(
123                     network::mojom::WebClientHintsType::kViewportWidth),
124                 preload_request_->Preferences().ShouldSend(
125                     network::mojom::WebClientHintsType::kViewportWidth));
126     }
127   }
128 
PreloadRequestVerification(ResourceType type,const char * url,const char * base_url,int width,network::mojom::ReferrerPolicy referrer_policy)129   void PreloadRequestVerification(
130       ResourceType type,
131       const char* url,
132       const char* base_url,
133       int width,
134       network::mojom::ReferrerPolicy referrer_policy) {
135     PreloadRequestVerification(type, url, base_url, width,
136                                ClientHintsPreferences());
137     EXPECT_EQ(referrer_policy, preload_request_->GetReferrerPolicy());
138   }
139 
PreloadRequestVerification(ResourceType type,const char * url,const char * base_url,int width,network::mojom::ReferrerPolicy referrer_policy,Document * document,const char * expected_referrer)140   void PreloadRequestVerification(
141       ResourceType type,
142       const char* url,
143       const char* base_url,
144       int width,
145       network::mojom::ReferrerPolicy referrer_policy,
146       Document* document,
147       const char* expected_referrer) {
148     PreloadRequestVerification(type, url, base_url, width, referrer_policy);
149     Resource* resource = preload_request_->Start(document);
150     ASSERT_TRUE(resource);
151     EXPECT_EQ(expected_referrer,
152               resource->GetResourceRequest().ReferrerString());
153   }
154 
PreconnectRequestVerification(const String & host,CrossOriginAttributeValue cross_origin)155   void PreconnectRequestVerification(const String& host,
156                                      CrossOriginAttributeValue cross_origin) {
157     if (!host.IsNull()) {
158       EXPECT_TRUE(preload_request_->IsPreconnect());
159       EXPECT_EQ(preload_request_->ResourceURL(), host);
160       EXPECT_EQ(preload_request_->CrossOrigin(), cross_origin);
161     }
162   }
163 
CorsRequestVerification(Document * document,network::mojom::RequestMode request_mode,network::mojom::CredentialsMode credentials_mode)164   void CorsRequestVerification(
165       Document* document,
166       network::mojom::RequestMode request_mode,
167       network::mojom::CredentialsMode credentials_mode) {
168     ASSERT_TRUE(preload_request_.get());
169     Resource* resource = preload_request_->Start(document);
170     ASSERT_TRUE(resource);
171     EXPECT_EQ(request_mode, resource->GetResourceRequest().GetMode());
172     EXPECT_EQ(credentials_mode,
173               resource->GetResourceRequest().GetCredentialsMode());
174   }
175 
NonceRequestVerification(const char * nonce)176   void NonceRequestVerification(const char* nonce) {
177     ASSERT_TRUE(preload_request_.get());
178     if (strlen(nonce))
179       EXPECT_EQ(nonce, preload_request_->Nonce());
180     else
181       EXPECT_TRUE(preload_request_->Nonce().IsEmpty());
182   }
183 
ContextVerification(bool is_image_set)184   void ContextVerification(bool is_image_set) {
185     ASSERT_TRUE(preload_request_.get());
186     EXPECT_EQ(preload_request_->IsImageSetForTestingOnly(), is_image_set);
187   }
188 
CheckNumberOfIntegrityConstraints(size_t expected)189   void CheckNumberOfIntegrityConstraints(size_t expected) {
190     size_t actual = 0;
191     if (preload_request_) {
192       actual = preload_request_->IntegrityMetadataForTestingOnly().size();
193       EXPECT_EQ(expected, actual);
194     }
195   }
196 
LazyLoadImageEnabledVerification(bool expected_enabled)197   void LazyLoadImageEnabledVerification(bool expected_enabled) {
198     if (expected_enabled) {
199       EXPECT_FALSE(preload_request_) << preload_request_->ResourceURL();
200     } else {
201       ASSERT_TRUE(preload_request_.get());
202     }
203   }
204 
205  protected:
Preload(std::unique_ptr<PreloadRequest> preload_request)206   void Preload(std::unique_ptr<PreloadRequest> preload_request) override {
207     preload_request_ = std::move(preload_request);
208   }
209 
210  private:
211   std::unique_ptr<PreloadRequest> preload_request_;
212 };
213 
214 class HTMLPreloadScannerTest : public PageTestBase {
215  protected:
216   enum ViewportState {
217     kViewportEnabled,
218     kViewportDisabled,
219   };
220 
221   enum PreloadState {
222     kPreloadEnabled,
223     kPreloadDisabled,
224   };
225 
CreateMediaValuesData()226   MediaValuesCached::MediaValuesCachedData CreateMediaValuesData() {
227     MediaValuesCached::MediaValuesCachedData data;
228     data.viewport_width = 500;
229     data.viewport_height = 600;
230     data.device_width = 700;
231     data.device_height = 800;
232     data.device_pixel_ratio = 2.0;
233     data.color_bits_per_component = 24;
234     data.monochrome_bits_per_component = 0;
235     data.primary_pointer_type = ui::POINTER_TYPE_FINE;
236     data.default_font_size = 16;
237     data.three_d_enabled = true;
238     data.media_type = media_type_names::kScreen;
239     data.strict_mode = true;
240     data.display_mode = blink::mojom::DisplayMode::kBrowser;
241     return data;
242   }
243 
RunSetUp(ViewportState viewport_state,PreloadState preload_state=kPreloadEnabled,network::mojom::ReferrerPolicy document_referrer_policy=network::mojom::ReferrerPolicy::kDefault,bool use_secure_document_url=false)244   void RunSetUp(ViewportState viewport_state,
245                 PreloadState preload_state = kPreloadEnabled,
246                 network::mojom::ReferrerPolicy document_referrer_policy =
247                     network::mojom::ReferrerPolicy::kDefault,
248                 bool use_secure_document_url = false) {
249     HTMLParserOptions options(&GetDocument());
250     KURL document_url = KURL("http://whatever.test/");
251     if (use_secure_document_url)
252       document_url = KURL("https://whatever.test/");
253     NavigateTo(document_url);
254     GetDocument().GetSettings()->SetViewportEnabled(viewport_state ==
255                                                     kViewportEnabled);
256     GetDocument().GetSettings()->SetViewportMetaEnabled(viewport_state ==
257                                                         kViewportEnabled);
258     GetDocument().GetSettings()->SetDoHtmlPreloadScanning(preload_state ==
259                                                           kPreloadEnabled);
260     GetFrame().DomWindow()->SetReferrerPolicy(document_referrer_policy);
261     scanner_ = std::make_unique<HTMLPreloadScanner>(
262         options, document_url,
263         std::make_unique<CachedDocumentParameters>(&GetDocument()),
264         CreateMediaValuesData(),
265         TokenPreloadScanner::ScannerType::kMainDocument);
266   }
267 
SetUp()268   void SetUp() override {
269     PageTestBase::SetUp(IntSize());
270     RunSetUp(kViewportEnabled);
271   }
272 
Test(PreloadScannerTestCase test_case)273   void Test(PreloadScannerTestCase test_case) {
274     SCOPED_TRACE(test_case.input_html);
275     HTMLMockHTMLResourcePreloader preloader;
276     KURL base_url(test_case.base_url);
277     scanner_->AppendToEnd(String(test_case.input_html));
278     PreloadRequestStream requests =
279         scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
280     preloader.TakeAndPreload(requests);
281 
282     preloader.PreloadRequestVerification(
283         test_case.type, test_case.preloaded_url, test_case.output_base_url,
284         test_case.resource_width, test_case.preferences);
285   }
286 
Test(HTMLPreconnectTestCase test_case)287   void Test(HTMLPreconnectTestCase test_case) {
288     HTMLMockHTMLResourcePreloader preloader;
289     KURL base_url(test_case.base_url);
290     scanner_->AppendToEnd(String(test_case.input_html));
291     PreloadRequestStream requests =
292         scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
293     preloader.TakeAndPreload(requests);
294     preloader.PreconnectRequestVerification(test_case.preconnected_host,
295                                             test_case.cross_origin);
296   }
297 
Test(ReferrerPolicyTestCase test_case)298   void Test(ReferrerPolicyTestCase test_case) {
299     HTMLMockHTMLResourcePreloader preloader;
300     KURL base_url(test_case.base_url);
301     scanner_->AppendToEnd(String(test_case.input_html));
302     PreloadRequestStream requests =
303         scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
304     preloader.TakeAndPreload(requests);
305 
306     if (test_case.expected_referrer) {
307       preloader.PreloadRequestVerification(
308           test_case.type, test_case.preloaded_url, test_case.output_base_url,
309           test_case.resource_width, test_case.referrer_policy, &GetDocument(),
310           test_case.expected_referrer);
311     } else {
312       preloader.PreloadRequestVerification(
313           test_case.type, test_case.preloaded_url, test_case.output_base_url,
314           test_case.resource_width, test_case.referrer_policy);
315     }
316   }
317 
Test(CorsTestCase test_case)318   void Test(CorsTestCase test_case) {
319     HTMLMockHTMLResourcePreloader preloader;
320     KURL base_url(test_case.base_url);
321     scanner_->AppendToEnd(String(test_case.input_html));
322     PreloadRequestStream requests =
323         scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
324     preloader.TakeAndPreload(requests);
325     preloader.CorsRequestVerification(&GetDocument(), test_case.request_mode,
326                                       test_case.credentials_mode);
327   }
328 
Test(CSPTestCase test_case)329   void Test(CSPTestCase test_case) {
330     HTMLMockHTMLResourcePreloader preloader;
331     KURL base_url(test_case.base_url);
332     seen_csp_meta_tag_ = false;
333     scanner_->AppendToEnd(String(test_case.input_html));
334     scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
335     EXPECT_EQ(test_case.should_see_csp_tag, seen_csp_meta_tag_);
336   }
337 
Test(NonceTestCase test_case)338   void Test(NonceTestCase test_case) {
339     HTMLMockHTMLResourcePreloader preloader;
340     KURL base_url(test_case.base_url);
341     scanner_->AppendToEnd(String(test_case.input_html));
342     PreloadRequestStream requests =
343         scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
344     preloader.TakeAndPreload(requests);
345     preloader.NonceRequestVerification(test_case.nonce);
346   }
347 
Test(ContextTestCase test_case)348   void Test(ContextTestCase test_case) {
349     HTMLMockHTMLResourcePreloader preloader;
350     KURL base_url(test_case.base_url);
351     scanner_->AppendToEnd(String(test_case.input_html));
352     PreloadRequestStream requests =
353         scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
354     preloader.TakeAndPreload(requests);
355 
356     preloader.ContextVerification(test_case.is_image_set);
357   }
358 
Test(IntegrityTestCase test_case)359   void Test(IntegrityTestCase test_case) {
360     SCOPED_TRACE(test_case.input_html);
361     HTMLMockHTMLResourcePreloader preloader;
362     KURL base_url("http://example.test/");
363     scanner_->AppendToEnd(String(test_case.input_html));
364     PreloadRequestStream requests =
365         scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
366     preloader.TakeAndPreload(requests);
367 
368     preloader.CheckNumberOfIntegrityConstraints(
369         test_case.number_of_integrity_metadata_found);
370   }
371 
Test(LazyLoadImageTestCase test_case)372   void Test(LazyLoadImageTestCase test_case) {
373     SCOPED_TRACE(test_case.input_html);
374     HTMLMockHTMLResourcePreloader preloader;
375     KURL base_url("http://example.test/");
376     scanner_->AppendToEnd(String(test_case.input_html));
377     PreloadRequestStream requests =
378         scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
379     preloader.TakeAndPreload(requests);
380     preloader.LazyLoadImageEnabledVerification(
381         test_case.lazy_load_image_enabled);
382   }
383 
384  private:
385   std::unique_ptr<HTMLPreloadScanner> scanner_;
386   bool seen_csp_meta_tag_ = false;
387 };
388 
TEST_F(HTMLPreloadScannerTest,testImages)389 TEST_F(HTMLPreloadScannerTest, testImages) {
390   PreloadScannerTestCase test_cases[] = {
391       {"http://example.test", "<img src='bla.gif'>", "bla.gif",
392        "http://example.test/", ResourceType::kImage, 0},
393       {"http://example.test", "<img srcset='bla.gif 320w, blabla.gif 640w'>",
394        "blabla.gif", "http://example.test/", ResourceType::kImage, 0},
395       {"http://example.test", "<img sizes='50vw' src='bla.gif'>", "bla.gif",
396        "http://example.test/", ResourceType::kImage, 250},
397       {"http://example.test",
398        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 1x'>", "bla2.gif",
399        "http://example.test/", ResourceType::kImage, 250},
400       {"http://example.test",
401        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 0.5x'>", "bla.gif",
402        "http://example.test/", ResourceType::kImage, 250},
403       {"http://example.test",
404        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 100w'>", "bla2.gif",
405        "http://example.test/", ResourceType::kImage, 250},
406       {"http://example.test",
407        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 100w, bla3.gif 250w'>",
408        "bla3.gif", "http://example.test/", ResourceType::kImage, 250},
409       {"http://example.test",
410        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 100w, bla3.gif 250w, "
411        "bla4.gif 500w'>",
412        "bla4.gif", "http://example.test/", ResourceType::kImage, 250},
413       {"http://example.test",
414        "<img src='bla.gif' srcset='bla2.gif 100w, bla3.gif 250w, bla4.gif "
415        "500w' sizes='50vw'>",
416        "bla4.gif", "http://example.test/", ResourceType::kImage, 250},
417       {"http://example.test",
418        "<img src='bla.gif' sizes='50vw' srcset='bla2.gif 100w, bla3.gif 250w, "
419        "bla4.gif 500w'>",
420        "bla4.gif", "http://example.test/", ResourceType::kImage, 250},
421       {"http://example.test",
422        "<img sizes='50vw' srcset='bla2.gif 100w, bla3.gif 250w, bla4.gif 500w' "
423        "src='bla.gif'>",
424        "bla4.gif", "http://example.test/", ResourceType::kImage, 250},
425       {"http://example.test",
426        "<img srcset='bla2.gif 100w, bla3.gif 250w, bla4.gif 500w' "
427        "src='bla.gif' sizes='50vw'>",
428        "bla4.gif", "http://example.test/", ResourceType::kImage, 250},
429       {"http://example.test",
430        "<img srcset='bla2.gif 100w, bla3.gif 250w, bla4.gif 500w' sizes='50vw' "
431        "src='bla.gif'>",
432        "bla4.gif", "http://example.test/", ResourceType::kImage, 250},
433       {"http://example.test",
434        "<img src='bla.gif' srcset='bla2.gif 100w, bla3.gif 250w, bla4.gif "
435        "500w'>",
436        "bla4.gif", "http://example.test/", ResourceType::kImage, 0},
437   };
438 
439   for (const auto& test_case : test_cases)
440     Test(test_case);
441 }
442 
TEST_F(HTMLPreloadScannerTest,testImagesWithViewport)443 TEST_F(HTMLPreloadScannerTest, testImagesWithViewport) {
444   PreloadScannerTestCase test_cases[] = {
445       {"http://example.test",
446        "<meta name=viewport content='width=160'><img srcset='bla.gif 320w, "
447        "blabla.gif 640w'>",
448        "bla.gif", "http://example.test/", ResourceType::kImage, 0},
449       {"http://example.test", "<img src='bla.gif'>", "bla.gif",
450        "http://example.test/", ResourceType::kImage, 0},
451       {"http://example.test", "<img sizes='50vw' src='bla.gif'>", "bla.gif",
452        "http://example.test/", ResourceType::kImage, 80},
453       {"http://example.test",
454        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 1x'>", "bla2.gif",
455        "http://example.test/", ResourceType::kImage, 80},
456       {"http://example.test",
457        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 0.5x'>", "bla.gif",
458        "http://example.test/", ResourceType::kImage, 80},
459       {"http://example.test",
460        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 160w'>", "bla2.gif",
461        "http://example.test/", ResourceType::kImage, 80},
462       {"http://example.test",
463        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 160w, bla3.gif 250w'>",
464        "bla2.gif", "http://example.test/", ResourceType::kImage, 80},
465       {"http://example.test",
466        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 160w, bla3.gif 250w, "
467        "bla4.gif 500w'>",
468        "bla2.gif", "http://example.test/", ResourceType::kImage, 80},
469       {"http://example.test",
470        "<img src='bla.gif' srcset='bla2.gif 160w, bla3.gif 250w, bla4.gif "
471        "500w' sizes='50vw'>",
472        "bla2.gif", "http://example.test/", ResourceType::kImage, 80},
473       {"http://example.test",
474        "<img src='bla.gif' sizes='50vw' srcset='bla2.gif 160w, bla3.gif 250w, "
475        "bla4.gif 500w'>",
476        "bla2.gif", "http://example.test/", ResourceType::kImage, 80},
477       {"http://example.test",
478        "<img sizes='50vw' srcset='bla2.gif 160w, bla3.gif 250w, bla4.gif 500w' "
479        "src='bla.gif'>",
480        "bla2.gif", "http://example.test/", ResourceType::kImage, 80},
481       {"http://example.test",
482        "<img srcset='bla2.gif 160w, bla3.gif 250w, bla4.gif 500w' "
483        "src='bla.gif' sizes='50vw'>",
484        "bla2.gif", "http://example.test/", ResourceType::kImage, 80},
485       {"http://example.test",
486        "<img srcset='bla2.gif 160w, bla3.gif 250w, bla4.gif 500w' sizes='50vw' "
487        "src='bla.gif'>",
488        "bla2.gif", "http://example.test/", ResourceType::kImage, 80},
489   };
490 
491   for (const auto& test_case : test_cases)
492     Test(test_case);
493 }
494 
TEST_F(HTMLPreloadScannerTest,testImagesWithViewportDeviceWidth)495 TEST_F(HTMLPreloadScannerTest, testImagesWithViewportDeviceWidth) {
496   PreloadScannerTestCase test_cases[] = {
497       {"http://example.test",
498        "<meta name=viewport content='width=device-width'><img srcset='bla.gif "
499        "320w, blabla.gif 640w'>",
500        "blabla.gif", "http://example.test/", ResourceType::kImage, 0},
501       {"http://example.test", "<img src='bla.gif'>", "bla.gif",
502        "http://example.test/", ResourceType::kImage, 0},
503       {"http://example.test", "<img sizes='50vw' src='bla.gif'>", "bla.gif",
504        "http://example.test/", ResourceType::kImage, 350},
505       {"http://example.test",
506        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 1x'>", "bla2.gif",
507        "http://example.test/", ResourceType::kImage, 350},
508       {"http://example.test",
509        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 0.5x'>", "bla.gif",
510        "http://example.test/", ResourceType::kImage, 350},
511       {"http://example.test",
512        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 160w'>", "bla2.gif",
513        "http://example.test/", ResourceType::kImage, 350},
514       {"http://example.test",
515        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 160w, bla3.gif 250w'>",
516        "bla3.gif", "http://example.test/", ResourceType::kImage, 350},
517       {"http://example.test",
518        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 160w, bla3.gif 250w, "
519        "bla4.gif 500w'>",
520        "bla4.gif", "http://example.test/", ResourceType::kImage, 350},
521       {"http://example.test",
522        "<img src='bla.gif' srcset='bla2.gif 160w, bla3.gif 250w, bla4.gif "
523        "500w' sizes='50vw'>",
524        "bla4.gif", "http://example.test/", ResourceType::kImage, 350},
525       {"http://example.test",
526        "<img src='bla.gif' sizes='50vw' srcset='bla2.gif 160w, bla3.gif 250w, "
527        "bla4.gif 500w'>",
528        "bla4.gif", "http://example.test/", ResourceType::kImage, 350},
529       {"http://example.test",
530        "<img sizes='50vw' srcset='bla2.gif 160w, bla3.gif 250w, bla4.gif 500w' "
531        "src='bla.gif'>",
532        "bla4.gif", "http://example.test/", ResourceType::kImage, 350},
533       {"http://example.test",
534        "<img srcset='bla2.gif 160w, bla3.gif 250w, bla4.gif 500w' "
535        "src='bla.gif' sizes='50vw'>",
536        "bla4.gif", "http://example.test/", ResourceType::kImage, 350},
537       {"http://example.test",
538        "<img srcset='bla2.gif 160w, bla3.gif 250w, bla4.gif 500w' sizes='50vw' "
539        "src='bla.gif'>",
540        "bla4.gif", "http://example.test/", ResourceType::kImage, 350},
541   };
542 
543   for (const auto& test_case : test_cases)
544     Test(test_case);
545 }
546 
TEST_F(HTMLPreloadScannerTest,testImagesWithViewportDisabled)547 TEST_F(HTMLPreloadScannerTest, testImagesWithViewportDisabled) {
548   RunSetUp(kViewportDisabled);
549   PreloadScannerTestCase test_cases[] = {
550       {"http://example.test",
551        "<meta name=viewport content='width=160'><img src='bla.gif'>", "bla.gif",
552        "http://example.test/", ResourceType::kImage, 0},
553       {"http://example.test", "<img srcset='bla.gif 320w, blabla.gif 640w'>",
554        "blabla.gif", "http://example.test/", ResourceType::kImage, 0},
555       {"http://example.test", "<img sizes='50vw' src='bla.gif'>", "bla.gif",
556        "http://example.test/", ResourceType::kImage, 250},
557       {"http://example.test",
558        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 1x'>", "bla2.gif",
559        "http://example.test/", ResourceType::kImage, 250},
560       {"http://example.test",
561        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 0.5x'>", "bla.gif",
562        "http://example.test/", ResourceType::kImage, 250},
563       {"http://example.test",
564        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 100w'>", "bla2.gif",
565        "http://example.test/", ResourceType::kImage, 250},
566       {"http://example.test",
567        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 100w, bla3.gif 250w'>",
568        "bla3.gif", "http://example.test/", ResourceType::kImage, 250},
569       {"http://example.test",
570        "<img sizes='50vw' src='bla.gif' srcset='bla2.gif 100w, bla3.gif 250w, "
571        "bla4.gif 500w'>",
572        "bla4.gif", "http://example.test/", ResourceType::kImage, 250},
573       {"http://example.test",
574        "<img src='bla.gif' srcset='bla2.gif 100w, bla3.gif 250w, bla4.gif "
575        "500w' sizes='50vw'>",
576        "bla4.gif", "http://example.test/", ResourceType::kImage, 250},
577       {"http://example.test",
578        "<img src='bla.gif' sizes='50vw' srcset='bla2.gif 100w, bla3.gif 250w, "
579        "bla4.gif 500w'>",
580        "bla4.gif", "http://example.test/", ResourceType::kImage, 250},
581       {"http://example.test",
582        "<img sizes='50vw' srcset='bla2.gif 100w, bla3.gif 250w, bla4.gif 500w' "
583        "src='bla.gif'>",
584        "bla4.gif", "http://example.test/", ResourceType::kImage, 250},
585       {"http://example.test",
586        "<img srcset='bla2.gif 100w, bla3.gif 250w, bla4.gif 500w' "
587        "src='bla.gif' sizes='50vw'>",
588        "bla4.gif", "http://example.test/", ResourceType::kImage, 250},
589       {"http://example.test",
590        "<img srcset='bla2.gif 100w, bla3.gif 250w, bla4.gif 500w' sizes='50vw' "
591        "src='bla.gif'>",
592        "bla4.gif", "http://example.test/", ResourceType::kImage, 250},
593   };
594 
595   for (const auto& test_case : test_cases)
596     Test(test_case);
597 }
598 
TEST_F(HTMLPreloadScannerTest,testViewportNoContent)599 TEST_F(HTMLPreloadScannerTest, testViewportNoContent) {
600   PreloadScannerTestCase test_cases[] = {
601       {"http://example.test",
602        "<meta name=viewport><img srcset='bla.gif 320w, blabla.gif 640w'>",
603        "blabla.gif", "http://example.test/", ResourceType::kImage, 0},
604       {"http://example.test",
605        "<meta name=viewport content=sdkbsdkjnejjha><img srcset='bla.gif 320w, "
606        "blabla.gif 640w'>",
607        "blabla.gif", "http://example.test/", ResourceType::kImage, 0},
608   };
609 
610   for (const auto& test_case : test_cases)
611     Test(test_case);
612 }
613 
TEST_F(HTMLPreloadScannerTest,testMetaAcceptCH)614 TEST_F(HTMLPreloadScannerTest, testMetaAcceptCH) {
615   ClientHintsPreferences dpr;
616   ClientHintsPreferences resource_width;
617   ClientHintsPreferences all;
618   ClientHintsPreferences viewport_width;
619   dpr.SetShouldSend(network::mojom::WebClientHintsType::kDpr);
620   all.SetShouldSend(network::mojom::WebClientHintsType::kDpr);
621   resource_width.SetShouldSend(
622       network::mojom::WebClientHintsType::kResourceWidth);
623   all.SetShouldSend(network::mojom::WebClientHintsType::kResourceWidth);
624   viewport_width.SetShouldSend(
625       network::mojom::WebClientHintsType::kViewportWidth);
626   all.SetShouldSend(network::mojom::WebClientHintsType::kViewportWidth);
627   PreloadScannerTestCase test_cases[] = {
628       {"http://example.test",
629        "<meta http-equiv='accept-ch' content='bla'><img srcset='bla.gif 320w, "
630        "blabla.gif 640w'>",
631        "blabla.gif", "http://example.test/", ResourceType::kImage, 0},
632       {"http://example.test",
633        "<meta http-equiv='accept-ch' content='dprw'><img srcset='bla.gif 320w, "
634        "blabla.gif 640w'>",
635        "blabla.gif", "http://example.test/", ResourceType::kImage, 0},
636       {"http://example.test",
637        "<meta http-equiv='accept-ch'><img srcset='bla.gif 320w, blabla.gif "
638        "640w'>",
639        "blabla.gif", "http://example.test/", ResourceType::kImage, 0},
640       {"http://example.test",
641        "<meta http-equiv='accept-ch' content='dpr  '><img srcset='bla.gif "
642        "320w, blabla.gif 640w'>",
643        "blabla.gif", "http://example.test/", ResourceType::kImage, 0, dpr},
644       {"http://example.test",
645        "<meta http-equiv='accept-ch' content='bla,dpr  '><img srcset='bla.gif "
646        "320w, blabla.gif 640w'>",
647        "blabla.gif", "http://example.test/", ResourceType::kImage, 0, dpr},
648       {"http://example.test",
649        "<meta http-equiv='accept-ch' content='  width  '><img sizes='100vw' "
650        "srcset='bla.gif 320w, blabla.gif 640w'>",
651        "blabla.gif", "http://example.test/", ResourceType::kImage, 500,
652        resource_width},
653       {"http://example.test",
654        "<meta http-equiv='accept-ch' content='  width  , wutever'><img "
655        "sizes='300px' srcset='bla.gif 320w, blabla.gif 640w'>",
656        "blabla.gif", "http://example.test/", ResourceType::kImage, 300,
657        resource_width},
658       {"http://example.test",
659        "<meta http-equiv='accept-ch' content='  viewport-width  '><img "
660        "srcset='bla.gif 320w, blabla.gif 640w'>",
661        "blabla.gif", "http://example.test/", ResourceType::kImage, 0,
662        viewport_width},
663       {"http://example.test",
664        "<meta http-equiv='accept-ch' content='  viewport-width  , "
665        "wutever'><img srcset='bla.gif 320w, blabla.gif 640w'>",
666        "blabla.gif", "http://example.test/", ResourceType::kImage, 0,
667        viewport_width},
668       {"http://example.test",
669        "<meta http-equiv='accept-ch' content='  viewport-width  ,width, "
670        "wutever, dpr  '><img sizes='90vw' srcset='bla.gif 320w, blabla.gif "
671        "640w'>",
672        "blabla.gif", "http://example.test/", ResourceType::kImage, 450, all},
673   };
674 
675   for (const auto& test_case : test_cases) {
676     RunSetUp(kViewportDisabled, kPreloadEnabled,
677              network::mojom::ReferrerPolicy::kDefault,
678              true /* use_secure_document_url */);
679     Test(test_case);
680   }
681 }
682 
TEST_F(HTMLPreloadScannerTest,testMetaAcceptCHInsecureDocument)683 TEST_F(HTMLPreloadScannerTest, testMetaAcceptCHInsecureDocument) {
684   ClientHintsPreferences all;
685   all.SetShouldSend(network::mojom::WebClientHintsType::kDpr);
686   all.SetShouldSend(network::mojom::WebClientHintsType::kResourceWidth);
687   all.SetShouldSend(network::mojom::WebClientHintsType::kViewportWidth);
688 
689   const PreloadScannerTestCase expect_no_client_hint = {
690       "http://example.test",
691       "<meta http-equiv='accept-ch' content='  viewport-width  ,width, "
692       "wutever, dpr  '><img sizes='90vw' srcset='bla.gif 320w, blabla.gif "
693       "640w'>",
694       "blabla.gif",
695       "http://example.test/",
696       ResourceType::kImage,
697       450};
698 
699   const PreloadScannerTestCase expect_client_hint = {
700       "http://example.test",
701       "<meta http-equiv='accept-ch' content='  viewport-width  ,width, "
702       "wutever, dpr  '><img sizes='90vw' srcset='bla.gif 320w, blabla.gif "
703       "640w'>",
704       "blabla.gif",
705       "http://example.test/",
706       ResourceType::kImage,
707       450,
708       all};
709 
710   // For an insecure document, client hint should not be attached.
711   RunSetUp(kViewportDisabled, kPreloadEnabled,
712            network::mojom::ReferrerPolicy::kDefault,
713            false /* use_secure_document_url */);
714   Test(expect_no_client_hint);
715 
716   // For a secure document, client hint should be attached.
717   RunSetUp(kViewportDisabled, kPreloadEnabled,
718            network::mojom::ReferrerPolicy::kDefault,
719            true /* use_secure_document_url */);
720   Test(expect_client_hint);
721 }
722 
TEST_F(HTMLPreloadScannerTest,testPreconnect)723 TEST_F(HTMLPreloadScannerTest, testPreconnect) {
724   HTMLPreconnectTestCase test_cases[] = {
725       {"http://example.test", "<link rel=preconnect href=http://example2.test>",
726        "http://example2.test", kCrossOriginAttributeNotSet},
727       {"http://example.test",
728        "<link rel=preconnect href=http://example2.test crossorigin=anonymous>",
729        "http://example2.test", kCrossOriginAttributeAnonymous},
730       {"http://example.test",
731        "<link rel=preconnect href=http://example2.test "
732        "crossorigin='use-credentials'>",
733        "http://example2.test", kCrossOriginAttributeUseCredentials},
734       {"http://example.test",
735        "<link rel=preconnected href=http://example2.test "
736        "crossorigin='use-credentials'>",
737        nullptr, kCrossOriginAttributeNotSet},
738       {"http://example.test",
739        "<link rel=preconnect href=ws://example2.test "
740        "crossorigin='use-credentials'>",
741        nullptr, kCrossOriginAttributeNotSet},
742   };
743 
744   for (const auto& test_case : test_cases)
745     Test(test_case);
746 }
747 
TEST_F(HTMLPreloadScannerTest,testDisables)748 TEST_F(HTMLPreloadScannerTest, testDisables) {
749   RunSetUp(kViewportEnabled, kPreloadDisabled);
750 
751   PreloadScannerTestCase test_cases[] = {
752       {"http://example.test", "<img src='bla.gif'>"},
753   };
754 
755   for (const auto& test_case : test_cases)
756     Test(test_case);
757 }
758 
TEST_F(HTMLPreloadScannerTest,testPicture)759 TEST_F(HTMLPreloadScannerTest, testPicture) {
760   PreloadScannerTestCase test_cases[] = {
761       {"http://example.test",
762        "<picture><source srcset='srcset_bla.gif'><img src='bla.gif'></picture>",
763        "srcset_bla.gif", "http://example.test/", ResourceType::kImage, 0},
764       {"http://example.test",
765        "<picture><source srcset='srcset_bla.gif' type=''><img "
766        "src='bla.gif'></picture>",
767        "srcset_bla.gif", "http://example.test/", ResourceType::kImage, 0},
768       {"http://example.test",
769        "<picture><source sizes='50vw' srcset='srcset_bla.gif'><img "
770        "src='bla.gif'></picture>",
771        "srcset_bla.gif", "http://example.test/", ResourceType::kImage, 250},
772       {"http://example.test",
773        "<picture><source sizes='50vw' srcset='srcset_bla.gif'><img "
774        "sizes='50vw' src='bla.gif'></picture>",
775        "srcset_bla.gif", "http://example.test/", ResourceType::kImage, 250},
776       {"http://example.test",
777        "<picture><source srcset='srcset_bla.gif' sizes='50vw'><img "
778        "sizes='50vw' src='bla.gif'></picture>",
779        "srcset_bla.gif", "http://example.test/", ResourceType::kImage, 250},
780       {"http://example.test",
781        "<picture><source srcset='srcset_bla.gif'><img sizes='50vw' "
782        "src='bla.gif'></picture>",
783        "srcset_bla.gif", "http://example.test/", ResourceType::kImage, 0},
784       {"http://example.test",
785        "<picture><source media='(max-width: 900px)' "
786        "srcset='srcset_bla.gif'><img sizes='50vw' srcset='bla.gif "
787        "500w'></picture>",
788        "srcset_bla.gif", "http://example.test/", ResourceType::kImage, 0},
789       {"http://example.test",
790        "<picture><source media='(max-width: 400px)' "
791        "srcset='srcset_bla.gif'><img sizes='50vw' srcset='bla.gif "
792        "500w'></picture>",
793        "bla.gif", "http://example.test/", ResourceType::kImage, 250},
794       {"http://example.test",
795        "<picture><source type='image/webp' srcset='srcset_bla.gif'><img "
796        "sizes='50vw' srcset='bla.gif 500w'></picture>",
797        "srcset_bla.gif", "http://example.test/", ResourceType::kImage, 0},
798       {"http://example.test",
799        "<picture><source type='image/jp2' srcset='srcset_bla.gif'><img "
800        "sizes='50vw' srcset='bla.gif 500w'></picture>",
801        "bla.gif", "http://example.test/", ResourceType::kImage, 250},
802       {"http://example.test",
803        "<picture><source media='(max-width: 900px)' type='image/jp2' "
804        "srcset='srcset_bla.gif'><img sizes='50vw' srcset='bla.gif "
805        "500w'></picture>",
806        "bla.gif", "http://example.test/", ResourceType::kImage, 250},
807       {"http://example.test",
808        "<picture><source type='image/webp' media='(max-width: 400px)' "
809        "srcset='srcset_bla.gif'><img sizes='50vw' srcset='bla.gif "
810        "500w'></picture>",
811        "bla.gif", "http://example.test/", ResourceType::kImage, 250},
812       {"http://example.test",
813        "<picture><source type='image/jp2' media='(max-width: 900px)' "
814        "srcset='srcset_bla.gif'><img sizes='50vw' srcset='bla.gif "
815        "500w'></picture>",
816        "bla.gif", "http://example.test/", ResourceType::kImage, 250},
817       {"http://example.test",
818        "<picture><source media='(max-width: 400px)' type='image/webp' "
819        "srcset='srcset_bla.gif'><img sizes='50vw' srcset='bla.gif "
820        "500w'></picture>",
821        "bla.gif", "http://example.test/", ResourceType::kImage, 250},
822   };
823 
824   for (const auto& test_case : test_cases)
825     Test(test_case);
826 }
827 
TEST_F(HTMLPreloadScannerTest,testContext)828 TEST_F(HTMLPreloadScannerTest, testContext) {
829   ContextTestCase test_cases[] = {
830       {"http://example.test",
831        "<picture><source srcset='srcset_bla.gif'><img src='bla.gif'></picture>",
832        "srcset_bla.gif", true},
833       {"http://example.test", "<img src='bla.gif'>", "bla.gif", false},
834       {"http://example.test", "<img srcset='bla.gif'>", "bla.gif", true},
835   };
836 
837   for (const auto& test_case : test_cases)
838     Test(test_case);
839 }
840 
TEST_F(HTMLPreloadScannerTest,testReferrerPolicy)841 TEST_F(HTMLPreloadScannerTest, testReferrerPolicy) {
842   ReferrerPolicyTestCase test_cases[] = {
843       {"http://example.test", "<img src='bla.gif'/>", "bla.gif",
844        "http://example.test/", ResourceType::kImage, 0,
845        network::mojom::ReferrerPolicy::kDefault},
846       {"http://example.test", "<img referrerpolicy='origin' src='bla.gif'/>",
847        "bla.gif", "http://example.test/", ResourceType::kImage, 0,
848        network::mojom::ReferrerPolicy::kOrigin, nullptr},
849       {"http://example.test",
850        "<meta name='referrer' content='not-a-valid-policy'><img "
851        "src='bla.gif'/>",
852        "bla.gif", "http://example.test/", ResourceType::kImage, 0,
853        network::mojom::ReferrerPolicy::kDefault, nullptr},
854       {"http://example.test",
855        "<img referrerpolicy='origin' referrerpolicy='origin-when-cross-origin' "
856        "src='bla.gif'/>",
857        "bla.gif", "http://example.test/", ResourceType::kImage, 0,
858        network::mojom::ReferrerPolicy::kOrigin, nullptr},
859       {"http://example.test",
860        "<img referrerpolicy='not-a-valid-policy' src='bla.gif'/>", "bla.gif",
861        "http://example.test/", ResourceType::kImage, 0,
862        network::mojom::ReferrerPolicy::kDefault, nullptr},
863       {"http://example.test",
864        "<link rel=preload as=image referrerpolicy='origin-when-cross-origin' "
865        "href='bla.gif'/>",
866        "bla.gif", "http://example.test/", ResourceType::kImage, 0,
867        network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin, nullptr},
868       {"http://example.test",
869        "<link rel=preload as=image referrerpolicy='same-origin' "
870        "href='bla.gif'/>",
871        "bla.gif", "http://example.test/", ResourceType::kImage, 0,
872        network::mojom::ReferrerPolicy::kSameOrigin, nullptr},
873       {"http://example.test",
874        "<link rel=preload as=image referrerpolicy='strict-origin' "
875        "href='bla.gif'/>",
876        "bla.gif", "http://example.test/", ResourceType::kImage, 0,
877        network::mojom::ReferrerPolicy::kStrictOrigin, nullptr},
878       {"http://example.test",
879        "<link rel=preload as=image "
880        "referrerpolicy='strict-origin-when-cross-origin' "
881        "href='bla.gif'/>",
882        "bla.gif", "http://example.test/", ResourceType::kImage, 0,
883        network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin, nullptr},
884       {"http://example.test",
885        "<link rel='stylesheet' href='sheet.css' type='text/css'>", "sheet.css",
886        "http://example.test/", ResourceType::kCSSStyleSheet, 0,
887        network::mojom::ReferrerPolicy::kDefault, nullptr},
888       {"http://example.test",
889        "<link rel=preload as=image referrerpolicy='origin' "
890        "referrerpolicy='origin-when-cross-origin' href='bla.gif'/>",
891        "bla.gif", "http://example.test/", ResourceType::kImage, 0,
892        network::mojom::ReferrerPolicy::kOrigin, nullptr},
893       {"http://example.test",
894        "<meta name='referrer' content='no-referrer'><img "
895        "referrerpolicy='origin' src='bla.gif'/>",
896        "bla.gif", "http://example.test/", ResourceType::kImage, 0,
897        network::mojom::ReferrerPolicy::kOrigin, nullptr},
898       // The scanner's state is not reset between test cases, so all subsequent
899       // test cases have a document referrer policy of no-referrer.
900       {"http://example.test",
901        "<link rel=preload as=image referrerpolicy='not-a-valid-policy' "
902        "href='bla.gif'/>",
903        "bla.gif", "http://example.test/", ResourceType::kImage, 0,
904        network::mojom::ReferrerPolicy::kNever, nullptr},
905       {"http://example.test",
906        "<img referrerpolicy='not-a-valid-policy' src='bla.gif'/>", "bla.gif",
907        "http://example.test/", ResourceType::kImage, 0,
908        network::mojom::ReferrerPolicy::kNever, nullptr},
909       {"http://example.test", "<img src='bla.gif'/>", "bla.gif",
910        "http://example.test/", ResourceType::kImage, 0,
911        network::mojom::ReferrerPolicy::kNever, nullptr}};
912 
913   for (const auto& test_case : test_cases)
914     Test(test_case);
915 }
916 
TEST_F(HTMLPreloadScannerTest,testCors)917 TEST_F(HTMLPreloadScannerTest, testCors) {
918   CorsTestCase test_cases[] = {
919       {"http://example.test", "<script src='/script'></script>",
920        network::mojom::RequestMode::kNoCors,
921        network::mojom::CredentialsMode::kInclude},
922       {"http://example.test", "<script crossorigin src='/script'></script>",
923        network::mojom::RequestMode::kCors,
924        network::mojom::CredentialsMode::kSameOrigin},
925       {"http://example.test",
926        "<script crossorigin=use-credentials src='/script'></script>",
927        network::mojom::RequestMode::kCors,
928        network::mojom::CredentialsMode::kInclude},
929       {"http://example.test", "<script type='module' src='/script'></script>",
930        network::mojom::RequestMode::kCors,
931        network::mojom::CredentialsMode::kSameOrigin},
932       {"http://example.test",
933        "<script type='module' crossorigin='anonymous' src='/script'></script>",
934        network::mojom::RequestMode::kCors,
935        network::mojom::CredentialsMode::kSameOrigin},
936       {"http://example.test",
937        "<script type='module' crossorigin='use-credentials' "
938        "src='/script'></script>",
939        network::mojom::RequestMode::kCors,
940        network::mojom::CredentialsMode::kInclude},
941   };
942 
943   for (const auto& test_case : test_cases) {
944     SCOPED_TRACE(test_case.input_html);
945     Test(test_case);
946   }
947 }
948 
TEST_F(HTMLPreloadScannerTest,testCSP)949 TEST_F(HTMLPreloadScannerTest, testCSP) {
950   CSPTestCase test_cases[] = {
951       {"http://example.test",
952        "<meta http-equiv=\"Content-Security-Policy\" content=\"default-src "
953        "https:\">",
954        true},
955       {"http://example.test",
956        "<meta name=\"viewport\" content=\"width=device-width\">", false},
957       {"http://example.test", "<img src=\"example.gif\">", false}};
958 
959   for (const auto& test_case : test_cases) {
960     SCOPED_TRACE(test_case.input_html);
961     Test(test_case);
962   }
963 }
964 
TEST_F(HTMLPreloadScannerTest,testNonce)965 TEST_F(HTMLPreloadScannerTest, testNonce) {
966   NonceTestCase test_cases[] = {
967       {"http://example.test", "<script src='/script'></script>", ""},
968       {"http://example.test", "<script src='/script' nonce=''></script>", ""},
969       {"http://example.test", "<script src='/script' nonce='abc'></script>",
970        "abc"},
971       {"http://example.test", "<link rel='import' href='/import'>", ""},
972       {"http://example.test", "<link rel='import' href='/import' nonce=''>",
973        ""},
974       {"http://example.test", "<link rel='import' href='/import' nonce='abc'>",
975        "abc"},
976       {"http://example.test", "<link rel='stylesheet' href='/style'>", ""},
977       {"http://example.test", "<link rel='stylesheet' href='/style' nonce=''>",
978        ""},
979       {"http://example.test",
980        "<link rel='stylesheet' href='/style' nonce='abc'>", "abc"},
981 
982       // <img> doesn't support nonces:
983       {"http://example.test", "<img src='/image'>", ""},
984       {"http://example.test", "<img src='/image' nonce=''>", ""},
985       {"http://example.test", "<img src='/image' nonce='abc'>", ""},
986   };
987 
988   for (const auto& test_case : test_cases) {
989     SCOPED_TRACE(test_case.input_html);
990     Test(test_case);
991   }
992 }
993 
994 // Tests that a document-level referrer policy (e.g. one set by HTTP header) is
995 // applied for preload requests.
TEST_F(HTMLPreloadScannerTest,testReferrerPolicyOnDocument)996 TEST_F(HTMLPreloadScannerTest, testReferrerPolicyOnDocument) {
997   RunSetUp(kViewportEnabled, kPreloadEnabled,
998            network::mojom::ReferrerPolicy::kOrigin);
999   ReferrerPolicyTestCase test_cases[] = {
1000       {"http://example.test", "<img src='blah.gif'/>", "blah.gif",
1001        "http://example.test/", ResourceType::kImage, 0,
1002        network::mojom::ReferrerPolicy::kOrigin, nullptr},
1003       {"http://example.test", "<style>@import url('blah.css');</style>",
1004        "blah.css", "http://example.test/", ResourceType::kCSSStyleSheet, 0,
1005        network::mojom::ReferrerPolicy::kOrigin, nullptr},
1006       // Tests that a meta-delivered referrer policy with an unrecognized policy
1007       // value does not override the document's referrer policy.
1008       {"http://example.test",
1009        "<meta name='referrer' content='not-a-valid-policy'><img "
1010        "src='bla.gif'/>",
1011        "bla.gif", "http://example.test/", ResourceType::kImage, 0,
1012        network::mojom::ReferrerPolicy::kOrigin, nullptr},
1013       // Tests that a meta-delivered referrer policy with a valid policy value
1014       // does override the document's referrer policy.
1015       {"http://example.test",
1016        "<meta name='referrer' content='unsafe-url'><img src='bla.gif'/>",
1017        "bla.gif", "http://example.test/", ResourceType::kImage, 0,
1018        network::mojom::ReferrerPolicy::kAlways, nullptr},
1019   };
1020 
1021   for (const auto& test_case : test_cases)
1022     Test(test_case);
1023 }
1024 
TEST_F(HTMLPreloadScannerTest,testLinkRelPreload)1025 TEST_F(HTMLPreloadScannerTest, testLinkRelPreload) {
1026   PreloadScannerTestCase test_cases[] = {
1027       {"http://example.test", "<link rel=preload as=fetch href=bla>", "bla",
1028        "http://example.test/", ResourceType::kRaw, 0},
1029       {"http://example.test", "<link rel=preload href=bla as=script>", "bla",
1030        "http://example.test/", ResourceType::kScript, 0},
1031       {"http://example.test",
1032        "<link rel=preload href=bla as=script type='script/foo'>", "bla",
1033        "http://example.test/", ResourceType::kScript, 0},
1034       {"http://example.test", "<link rel=preload href=bla as=style>", "bla",
1035        "http://example.test/", ResourceType::kCSSStyleSheet, 0},
1036       {"http://example.test",
1037        "<link rel=preload href=bla as=style type='text/css'>", "bla",
1038        "http://example.test/", ResourceType::kCSSStyleSheet, 0},
1039       {"http://example.test",
1040        "<link rel=preload href=bla as=style type='text/bla'>", nullptr,
1041        "http://example.test/", ResourceType::kCSSStyleSheet, 0},
1042       {"http://example.test", "<link rel=preload href=bla as=image>", "bla",
1043        "http://example.test/", ResourceType::kImage, 0},
1044       {"http://example.test",
1045        "<link rel=preload href=bla as=image type='image/webp'>", "bla",
1046        "http://example.test/", ResourceType::kImage, 0},
1047       {"http://example.test",
1048        "<link rel=preload href=bla as=image type='image/bla'>", nullptr,
1049        "http://example.test/", ResourceType::kImage, 0},
1050       {"http://example.test", "<link rel=preload href=bla as=font>", "bla",
1051        "http://example.test/", ResourceType::kFont, 0},
1052       {"http://example.test",
1053        "<link rel=preload href=bla as=font type='font/woff2'>", "bla",
1054        "http://example.test/", ResourceType::kFont, 0},
1055       {"http://example.test",
1056        "<link rel=preload href=bla as=font type='font/bla'>", nullptr,
1057        "http://example.test/", ResourceType::kFont, 0},
1058       // Until the preload cache is defined in terms of range requests and media
1059       // fetches we can't reliably preload audio/video content and expect it to
1060       // be served from the cache correctly. Until
1061       // https://github.com/w3c/preload/issues/97 is resolved and implemented we
1062       // need to disable these preloads.
1063       {"http://example.test", "<link rel=preload href=bla as=video>", nullptr,
1064        "http://example.test/", ResourceType::kVideo, 0},
1065       {"http://example.test", "<link rel=preload href=bla as=track>", "bla",
1066        "http://example.test/", ResourceType::kTextTrack, 0},
1067       {"http://example.test",
1068        "<link rel=preload href=bla as=image media=\"(max-width: 800px)\">",
1069        "bla", "http://example.test/", ResourceType::kImage, 0},
1070       {"http://example.test",
1071        "<link rel=preload href=bla as=image media=\"(max-width: 400px)\">",
1072        nullptr, "http://example.test/", ResourceType::kImage, 0},
1073       {"http://example.test", "<link rel=preload href=bla>", nullptr,
1074        "http://example.test/", ResourceType::kRaw, 0},
1075   };
1076 
1077   for (const auto& test_case : test_cases)
1078     Test(test_case);
1079 }
1080 
TEST_F(HTMLPreloadScannerTest,testNoDataUrls)1081 TEST_F(HTMLPreloadScannerTest, testNoDataUrls) {
1082   PreloadScannerTestCase test_cases[] = {
1083       {"http://example.test",
1084        "<link rel=preload href='data:text/html,<p>data</data>'>", nullptr,
1085        "http://example.test/", ResourceType::kRaw, 0},
1086       {"http://example.test", "<img src='data:text/html,<p>data</data>'>",
1087        nullptr, "http://example.test/", ResourceType::kImage, 0},
1088       {"data:text/html,<a>anchor</a>", "<img src='#anchor'>", nullptr,
1089        "http://example.test/", ResourceType::kImage, 0},
1090   };
1091 
1092   for (const auto& test_case : test_cases)
1093     Test(test_case);
1094 }
1095 
1096 // The preload scanner should follow the same policy that the ScriptLoader does
1097 // with regard to the type and language attribute.
TEST_F(HTMLPreloadScannerTest,testScriptTypeAndLanguage)1098 TEST_F(HTMLPreloadScannerTest, testScriptTypeAndLanguage) {
1099   PreloadScannerTestCase test_cases[] = {
1100       // Allow empty src and language attributes.
1101       {"http://example.test", "<script src='test.js'></script>", "test.js",
1102        "http://example.test/", ResourceType::kScript, 0},
1103       {"http://example.test",
1104        "<script type='' language='' src='test.js'></script>", "test.js",
1105        "http://example.test/", ResourceType::kScript, 0},
1106       // Allow standard language and type attributes.
1107       {"http://example.test",
1108        "<script type='text/javascript' src='test.js'></script>", "test.js",
1109        "http://example.test/", ResourceType::kScript, 0},
1110       {"http://example.test",
1111        "<script type='text/javascript' language='javascript' "
1112        "src='test.js'></script>",
1113        "test.js", "http://example.test/", ResourceType::kScript, 0},
1114       // Allow legacy languages in the "language" attribute with an empty
1115       // type.
1116       {"http://example.test",
1117        "<script language='javascript1.1' src='test.js'></script>", "test.js",
1118        "http://example.test/", ResourceType::kScript, 0},
1119       // Allow legacy languages in the "type" attribute.
1120       {"http://example.test",
1121        "<script type='javascript' src='test.js'></script>", "test.js",
1122        "http://example.test/", ResourceType::kScript, 0},
1123       {"http://example.test",
1124        "<script type='javascript1.7' src='test.js'></script>", "test.js",
1125        "http://example.test/", ResourceType::kScript, 0},
1126       // Do not allow invalid types in the "type" attribute.
1127       {"http://example.test", "<script type='invalid' src='test.js'></script>",
1128        nullptr, "http://example.test/", ResourceType::kScript, 0},
1129       {"http://example.test", "<script type='asdf' src='test.js'></script>",
1130        nullptr, "http://example.test/", ResourceType::kScript, 0},
1131       // Do not allow invalid languages.
1132       {"http://example.test",
1133        "<script language='french' src='test.js'></script>", nullptr,
1134        "http://example.test/", ResourceType::kScript, 0},
1135       {"http://example.test",
1136        "<script language='python' src='test.js'></script>", nullptr,
1137        "http://example.test/", ResourceType::kScript, 0},
1138   };
1139 
1140   for (const auto& test_case : test_cases)
1141     Test(test_case);
1142 }
1143 
1144 // Regression test for crbug.com/664744.
TEST_F(HTMLPreloadScannerTest,testUppercaseAsValues)1145 TEST_F(HTMLPreloadScannerTest, testUppercaseAsValues) {
1146   PreloadScannerTestCase test_cases[] = {
1147       {"http://example.test", "<link rel=preload href=bla as=SCRIPT>", "bla",
1148        "http://example.test/", ResourceType::kScript, 0},
1149       {"http://example.test", "<link rel=preload href=bla as=fOnT>", "bla",
1150        "http://example.test/", ResourceType::kFont, 0},
1151   };
1152 
1153   for (const auto& test_case : test_cases)
1154     Test(test_case);
1155 }
1156 
TEST_F(HTMLPreloadScannerTest,ReferrerHeader)1157 TEST_F(HTMLPreloadScannerTest, ReferrerHeader) {
1158   RunSetUp(kViewportEnabled, kPreloadEnabled,
1159            network::mojom::ReferrerPolicy::kAlways);
1160 
1161   KURL preload_url("http://example.test/sheet.css");
1162   // TODO(crbug.com/751425): We should use the mock functionality
1163   // via |PageTestBase::dummy_page_holder_|.
1164   url_test_helpers::RegisterMockedURLLoadWithCustomResponse(
1165       preload_url, "", WrappedResourceResponse(ResourceResponse()));
1166 
1167   ReferrerPolicyTestCase test_case = {
1168       "http://example.test",
1169       "<link rel='stylesheet' href='sheet.css' type='text/css'>",
1170       "sheet.css",
1171       "http://example.test/",
1172       ResourceType::kCSSStyleSheet,
1173       0,
1174       network::mojom::ReferrerPolicy::kAlways,
1175       "http://whatever.test/"};
1176   Test(test_case);
1177 }
1178 
TEST_F(HTMLPreloadScannerTest,Integrity)1179 TEST_F(HTMLPreloadScannerTest, Integrity) {
1180   IntegrityTestCase test_cases[] = {
1181       {0, "<script src=bla.js>"},
1182       {1,
1183        "<script src=bla.js "
1184        "integrity=sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng=>"},
1185       {0, "<script src=bla.js integrity=sha257-XXX>"},
1186       {2,
1187        "<script src=bla.js "
1188        "integrity=sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng= "
1189        "sha256-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxng=>"},
1190       {1,
1191        "<script src=bla.js "
1192        "integrity=sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng= "
1193        "integrity=sha257-XXXX>"},
1194       {0,
1195        "<script src=bla.js integrity=sha257-XXX "
1196        "integrity=sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng=>"},
1197   };
1198 
1199   for (const auto& test_case : test_cases)
1200     Test(test_case);
1201 }
1202 
1203 // Regression test for http://crbug.com/898795 where preloads after a
1204 // dynamically inserted meta csp tag are dispatched on subsequent calls to the
1205 // HTMLPreloadScanner, after they had been parsed.
TEST_F(HTMLPreloadScannerTest,MetaCsp_NoPreloadsAfter)1206 TEST_F(HTMLPreloadScannerTest, MetaCsp_NoPreloadsAfter) {
1207   PreloadScannerTestCase test_cases[] = {
1208       {"http://example.test",
1209        "<meta http-equiv='Content-Security-Policy'><link rel=preload href=bla "
1210        "as=SCRIPT>",
1211        nullptr, "http://example.test/", ResourceType::kScript, 0},
1212       // The buffered text referring to the preload above should be cleared, so
1213       // make sure it is not preloaded on subsequent calls to Scan.
1214       {"http://example.test", "", nullptr, "http://example.test/",
1215        ResourceType::kScript, 0},
1216   };
1217 
1218   for (const auto& test_case : test_cases)
1219     Test(test_case);
1220 }
1221 
TEST_F(HTMLPreloadScannerTest,LazyLoadImage_DisabledForSmallImages)1222 TEST_F(HTMLPreloadScannerTest, LazyLoadImage_DisabledForSmallImages) {
1223   ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test(true);
1224   ScopedAutomaticLazyImageLoadingForTest
1225       scoped_automatic_lazy_image_loading_for_test(true);
1226   ScopedRestrictAutomaticLazyImageLoadingToDataSaverForTest
1227       scoped_restrict_automatic_lazy_image_loading_to_data_saver_for_test(
1228           false);
1229   RunSetUp(kViewportEnabled);
1230   LazyLoadImageTestCase test_cases[] = {
1231       {"<img src='foo.jpg'>", true},
1232       {"<img src='foo.jpg' height='1px' width='1px'>", false},
1233       {"<img src='foo.jpg' style='height: 1px; width: 1px'>", false},
1234       {"<img src='foo.jpg' height='10px' width='10px'>", false},
1235       {"<img src='foo.jpg' style='height: 10px; width: 10px'>", false},
1236       {"<img src='foo.jpg' height='1px'>", true},
1237       {"<img src='foo.jpg' style='height: 1px;'>", true},
1238       {"<img src='foo.jpg' width='1px'>", true},
1239       {"<img src='foo.jpg' style='width: 1px;'>", true},
1240   };
1241 
1242   for (const auto& test_case : test_cases)
1243     Test(test_case);
1244 }
1245 
TEST_F(HTMLPreloadScannerTest,LazyLoadImage_FeatureDisabledWithAttribute)1246 TEST_F(HTMLPreloadScannerTest, LazyLoadImage_FeatureDisabledWithAttribute) {
1247   ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test(false);
1248   RunSetUp(kViewportEnabled);
1249   LazyLoadImageTestCase test_cases[] = {
1250       {"<img src='foo.jpg' loading='auto'>", false},
1251       {"<img src='foo.jpg' loading='lazy'>", false},
1252       {"<img src='foo.jpg' loading='eager'>", false},
1253   };
1254   for (const auto& test_case : test_cases)
1255     Test(test_case);
1256 }
1257 
TEST_F(HTMLPreloadScannerTest,LazyLoadImage_FeatureAutomaticEnabledWithAttribute)1258 TEST_F(HTMLPreloadScannerTest,
1259        LazyLoadImage_FeatureAutomaticEnabledWithAttribute) {
1260   ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test(true);
1261   ScopedAutomaticLazyImageLoadingForTest
1262       scoped_automatic_lazy_image_loading_for_test(true);
1263   ScopedRestrictAutomaticLazyImageLoadingToDataSaverForTest
1264       scoped_restrict_automatic_lazy_image_loading_to_data_saver_for_test(
1265           false);
1266   RunSetUp(kViewportEnabled);
1267   LazyLoadImageTestCase test_cases[] = {
1268       {"<img src='foo.jpg' loading='auto'>", true},
1269       {"<img src='foo.jpg' loading='lazy'>", true},
1270       {"<img src='foo.jpg' loading='eager'>", false},
1271       // loading=lazy should override other conditions.
1272       {"<img src='foo.jpg' style='height: 1px;' loading='lazy'>", true},
1273       {"<img src='foo.jpg' style='height: 1px; width: 1px' loading='lazy'>",
1274        true},
1275   };
1276   for (const auto& test_case : test_cases)
1277     Test(test_case);
1278 }
1279 
TEST_F(HTMLPreloadScannerTest,LazyLoadImage_FeatureExplicitEnabledWithAttribute)1280 TEST_F(HTMLPreloadScannerTest,
1281        LazyLoadImage_FeatureExplicitEnabledWithAttribute) {
1282   ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test(true);
1283   RunSetUp(kViewportEnabled);
1284   LazyLoadImageTestCase test_cases[] = {
1285       {"<img src='foo.jpg' loading='auto'>", false},
1286       {"<img src='foo.jpg' loading='lazy'>", true},
1287       {"<img src='foo.jpg' loading='eager'>", false},
1288   };
1289   for (const auto& test_case : test_cases)
1290     Test(test_case);
1291 }
1292 
TEST_F(HTMLPreloadScannerTest,LazyLoadImage_FeatureAutomaticPreloadForLargeImages)1293 TEST_F(HTMLPreloadScannerTest,
1294        LazyLoadImage_FeatureAutomaticPreloadForLargeImages) {
1295   // Large images should not be preloaded, when loading is auto or lazy.
1296   ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test(true);
1297   ScopedAutomaticLazyImageLoadingForTest
1298       scoped_automatic_lazy_image_loading_for_test(true);
1299   ScopedRestrictAutomaticLazyImageLoadingToDataSaverForTest
1300       scoped_restrict_automatic_lazy_image_loading_to_data_saver_for_test(
1301           false);
1302   RunSetUp(kViewportEnabled);
1303   PreloadScannerTestCase test_cases[] = {
1304       {"http://example.test", "<img src='foo.jpg' height='20px' width='20px'>",
1305        nullptr, "http://example.test/", ResourceType::kImage, 0},
1306       {"http://example.test",
1307        "<img src='foo.jpg' style='height: 20px; width: 20px'>", nullptr,
1308        "http://example.test/", ResourceType::kImage, 0},
1309       {"http://example.test",
1310        "<img src='foo.jpg' height='20px' width='20px' loading='lazy'>", nullptr,
1311        "http://example.test/", ResourceType::kImage, 0},
1312       {"http://example.test",
1313        "<img src='foo.jpg' height='20px' width='20px' loading='auto'>", nullptr,
1314        "http://example.test/", ResourceType::kImage, 0},
1315       {"http://example.test",
1316        "<img src='foo.jpg' style='height: 20px; width: 20px' loading='lazy'>",
1317        nullptr, "http://example.test/", ResourceType::kImage, 0},
1318       {"http://example.test",
1319        "<img src='foo.jpg' style='height: 20px; width: 20px' loading='auto'>",
1320        nullptr, "http://example.test/", ResourceType::kImage, 0},
1321   };
1322   for (const auto& test_case : test_cases)
1323     Test(test_case);
1324 
1325   // When loading is eager, lazyload is disabled and preload happens.
1326   LazyLoadImageTestCase test_cases_that_preload[] = {
1327       {"<img src='foo.jpg' height='20px' width='20px' loading='eager'>", false},
1328       {"<img src='foo.jpg' style='height: 20px; width: 20px' loading='eager'>",
1329        false},
1330   };
1331   for (const auto& test_case : test_cases_that_preload)
1332     Test(test_case);
1333 }
1334 
TEST_F(HTMLPreloadScannerTest,LazyLoadImage_FeatureExplicitPreloadForLargeImages)1335 TEST_F(HTMLPreloadScannerTest,
1336        LazyLoadImage_FeatureExplicitPreloadForLargeImages) {
1337   // Large images should not be preloaded, when loading is lazy.
1338   ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test(true);
1339   RunSetUp(kViewportEnabled);
1340   PreloadScannerTestCase test_cases[] = {
1341       {"http://example.test",
1342        "<img src='foo.jpg' height='20px' width='20px' loading='lazy'>", nullptr,
1343        "http://example.test/", ResourceType::kImage, 0},
1344       {"http://example.test",
1345        "<img src='foo.jpg' style='height: 20px; width: 20px' loading='lazy'>",
1346        nullptr, "http://example.test/", ResourceType::kImage, 0},
1347   };
1348   for (const auto& test_case : test_cases)
1349     Test(test_case);
1350 
1351   // When loading is eager or auto, lazyload is disabled and preload happens.
1352   LazyLoadImageTestCase test_cases_that_preload[] = {
1353       {"<img src='foo.jpg' height='20px' width='20px' loading='eager'>", false},
1354       {"<img src='foo.jpg' style='height: 20px; width: 20px' loading='eager'>",
1355        false},
1356       {"<img src='foo.jpg' height='20px' width='20px' loading='auto'>", false},
1357       {"<img src='foo.jpg' style='height: 20px; width: 20px' loading='auto'>",
1358        false},
1359   };
1360   for (const auto& test_case : test_cases_that_preload)
1361     Test(test_case);
1362 }
1363 
1364 // TODO(domfarolino): Before merging, can we just delete this test, since we no
1365 // longer have metadata fetching?
TEST_F(HTMLPreloadScannerTest,LazyLoadImage_DisableMetadataFetch)1366 TEST_F(HTMLPreloadScannerTest, LazyLoadImage_DisableMetadataFetch) {
1367   struct TestCase {
1368     bool automatic_lazy_image_loading_enabled;
1369     const char* loading_attr_value;
1370     bool expected_is_preload;
1371   };
1372   const TestCase test_cases[] = {
1373       // The lazyload eligible cases should not trigger a preload.
1374       {false, "lazy", false},
1375       {true, "lazy", false},
1376       {true, "auto", false},
1377 
1378       // Lazyload ineligible case.
1379       {false, "auto", true},
1380 
1381       // Full image should be fetched when loading='eager' irrespective of
1382       // automatic lazyload feature state.
1383       {false, "eager", true},
1384       {true, "eager", true},
1385   };
1386   for (const auto& test_case : test_cases) {
1387     ScopedAutomaticLazyImageLoadingForTest
1388         scoped_automatic_lazy_image_loading_for_test(
1389             test_case.automatic_lazy_image_loading_enabled);
1390     ScopedRestrictAutomaticLazyImageLoadingToDataSaverForTest
1391         scoped_restrict_automatic_lazy_image_loading_to_data_saver_for_test(
1392             false);
1393     RunSetUp(kViewportEnabled);
1394     const std::string img_html = base::StringPrintf(
1395         "<img src='foo.jpg' loading='%s'>", test_case.loading_attr_value);
1396     if (test_case.expected_is_preload) {
1397       LazyLoadImageTestCase test_preload = {img_html.c_str(), false};
1398       Test(test_preload);
1399     } else {
1400       PreloadScannerTestCase test_no_preload = {
1401           "http://example.test",  img_html.c_str(),     nullptr,
1402           "http://example.test/", ResourceType::kImage, 0};
1403       Test(test_no_preload);
1404     }
1405   }
1406 }
1407 
TEST_F(HTMLPreloadScannerTest,LazyLoadImage_FirstKImagesNotAppliesForExplicit)1408 TEST_F(HTMLPreloadScannerTest,
1409        LazyLoadImage_FirstKImagesNotAppliesForExplicit) {
1410   ScopedAutomaticLazyImageLoadingForTest
1411       scoped_automatic_lazy_image_loading_for_test(false);
1412   ScopedRestrictAutomaticLazyImageLoadingToDataSaverForTest
1413       scoped_restrict_automatic_lazy_image_loading_to_data_saver_for_test(
1414           false);
1415   GetDocument().GetSettings()->SetLazyImageFirstKFullyLoadUnknown(1);
1416   RunSetUp(kViewportEnabled);
1417 
1418   // Neither of the images should be preloaded.
1419   LazyLoadImageTestCase test1 = {"<img src='foo.jpg' loading='lazy'>", true};
1420   Test(test1);
1421   LazyLoadImageTestCase test2 = {"<img src='bar.jpg' loading='lazy'>", true};
1422   Test(test2);
1423 }
1424 
TEST_F(HTMLPreloadScannerTest,LazyLoadImage_FirstKImagesAppliesForAutomatic)1425 TEST_F(HTMLPreloadScannerTest, LazyLoadImage_FirstKImagesAppliesForAutomatic) {
1426   ScopedAutomaticLazyImageLoadingForTest
1427       scoped_automatic_lazy_image_loading_for_test(true);
1428   ScopedRestrictAutomaticLazyImageLoadingToDataSaverForTest
1429       scoped_restrict_automatic_lazy_image_loading_to_data_saver_for_test(
1430           false);
1431   GetDocument().GetSettings()->SetLazyImageFirstKFullyLoadUnknown(1);
1432   RunSetUp(kViewportEnabled);
1433 
1434   // Only the first image should get preloaded
1435   LazyLoadImageTestCase test1 = {"<img src='foo.jpg'>", false};
1436   Test(test1);
1437   LazyLoadImageTestCase test2 = {"<img src='bar.jpg'>", true};
1438   Test(test2);
1439 }
1440 
TEST_F(HTMLPreloadScannerTest,LazyLoadImage_ExplicitImageCountedForFirstKImages)1441 TEST_F(HTMLPreloadScannerTest,
1442        LazyLoadImage_ExplicitImageCountedForFirstKImages) {
1443   ScopedAutomaticLazyImageLoadingForTest
1444       scoped_automatic_lazy_image_loading_for_test(true);
1445   ScopedRestrictAutomaticLazyImageLoadingToDataSaverForTest
1446       scoped_restrict_automatic_lazy_image_loading_to_data_saver_for_test(
1447           false);
1448   GetDocument().GetSettings()->SetLazyImageFirstKFullyLoadUnknown(1);
1449   RunSetUp(kViewportEnabled);
1450 
1451   // The first image should be counted towards first K images limit, even though
1452   // it has loading='lazy'.
1453   LazyLoadImageTestCase test1 = {"<img src='foo.jpg' loading='lazy'>", true};
1454   Test(test1);
1455   LazyLoadImageTestCase test2 = {"<img src='bar.jpg'>", true};
1456   Test(test2);
1457 }
1458 
1459 // https://crbug.com/1087854
TEST_F(HTMLPreloadScannerTest,CSSImportWithSemicolonInUrl)1460 TEST_F(HTMLPreloadScannerTest, CSSImportWithSemicolonInUrl) {
1461   PreloadScannerTestCase test_cases[] = {
1462       {"https://example.test",
1463        "<style>@import "
1464        "url(\"https://example2.test/css?foo=a;b&bar=d\");</style>",
1465        "https://example2.test/css?foo=a;b&bar=d", "https://example.test/",
1466        ResourceType::kCSSStyleSheet, 0},
1467       {"https://example.test",
1468        "<style>@import "
1469        "url('https://example2.test/css?foo=a;b&bar=d');</style>",
1470        "https://example2.test/css?foo=a;b&bar=d", "https://example.test/",
1471        ResourceType::kCSSStyleSheet, 0},
1472       {"https://example.test",
1473        "<style>@import "
1474        "url(https://example2.test/css?foo=a;b&bar=d);</style>",
1475        "https://example2.test/css?foo=a;b&bar=d", "https://example.test/",
1476        ResourceType::kCSSStyleSheet, 0},
1477       {"https://example.test",
1478        "<style>@import \"https://example2.test/css?foo=a;b&bar=d\";</style>",
1479        "https://example2.test/css?foo=a;b&bar=d", "https://example.test/",
1480        ResourceType::kCSSStyleSheet, 0},
1481       {"https://example.test",
1482        "<style>@import 'https://example2.test/css?foo=a;b&bar=d';</style>",
1483        "https://example2.test/css?foo=a;b&bar=d", "https://example.test/",
1484        ResourceType::kCSSStyleSheet, 0},
1485   };
1486 
1487   for (const auto& test : test_cases)
1488     Test(test);
1489 }
1490 
1491 }  // namespace blink
1492