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