summaryrefslogtreecommitdiffstats
path: root/netwerk/test/browser/early_hint_preload_test_helper.jsm
blob: b60502fdffa04c88df68230df0e93b6ec9437031 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

const EXPORTED_SYMBOLS = [
  "request_count_checking",
  "test_hint_preload",
  "test_hint_preload_internal",
  "test_preload_hint_and_request",
];

const { Assert } = ChromeUtils.importESModule(
  "resource://testing-common/Assert.sys.mjs"
);
const { BrowserTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/BrowserTestUtils.sys.mjs"
);
const { gBrowser } = Services.wm.getMostRecentWindow("navigator:browser");

async function request_count_checking(testName, got, expected) {
  // stringify to pretty print assert output
  let g = JSON.stringify(got);
  let e = JSON.stringify(expected);
  // each early hint request can starts one hinted request, but doesn't yet
  // complete the early hint request during the test case
  Assert.ok(
    got.hinted == expected.hinted,
    `${testName}: unexpected amount of hinted request made expected ${expected.hinted} (${e}), got ${got.hinted} (${g})`
  );
  // when the early hint request doesn't complete fast enough, another request
  // is currently sent from the main document
  let expected_normal = expected.normal;
  Assert.ok(
    got.normal == expected_normal,
    `${testName}: unexpected amount of normal request made expected ${expected_normal} (${e}), got ${got.normal} (${g})`
  );
}

async function test_hint_preload(
  testName,
  requestFrom,
  imgUrl,
  expectedRequestCount,
  uuid = undefined
) {
  // generate a uuid if none were passed
  if (uuid == undefined) {
    uuid = Services.uuid.generateUUID();
  }
  await test_hint_preload_internal(
    testName,
    requestFrom,
    [[imgUrl, uuid.toString()]],
    expectedRequestCount
  );
}

// - testName is just there to be printed during Asserts when failing
// - the baseUrl can't have query strings, because they are currently used to pass
//   the early hint the server responds with
// - urls are in the form [[url1, uuid1], ...]. The uuids are there to make each preload
//   unique and not available in the cache from other test cases
// - expectedRequestCount is the sum of all requested objects { normal: count, hinted: count }
async function test_hint_preload_internal(
  testName,
  requestFrom,
  imgUrls,
  expectedRequestCount
) {
  // reset the count
  let headers = new Headers();
  headers.append("X-Early-Hint-Count-Start", "");
  await fetch(
    "http://example.com/browser/netwerk/test/browser/early_hint_pixel_count.sjs",
    { headers }
  );

  let requestUrl =
    requestFrom +
    "/browser/netwerk/test/browser/early_hint_main_html.sjs?" +
    new URLSearchParams(imgUrls).toString(); // encode the hinted images as query string

  await BrowserTestUtils.withNewTab(
    {
      gBrowser,
      url: requestUrl,
      waitForLoad: true,
    },
    async function() {}
  );

  let gotRequestCount = await fetch(
    "http://example.com/browser/netwerk/test/browser/early_hint_pixel_count.sjs"
  ).then(response => response.json());

  await request_count_checking(testName, gotRequestCount, expectedRequestCount);
}

// Verify that CSP policies in both the 103 response as well as the main response are respected.
// e.g.
// 103 Early Hint
// Content-Security-Policy: style-src: self;
// Link: </style.css>; rel=preload; as=style
// 200 OK
// Content-Security-Policy: style-src: none;
// Link: </font.ttf>; rel=preload; as=font

// Server-side we verify that:
//  - the hinted preload request was made as expected
//  - the load request request was made as expected
// Client-side, we verify that the image was loaded or not loaded, depending on the scenario

// This verifies preload hints and requests
async function test_preload_hint_and_request(input, expected_results) {
  // reset the count
  let headers = new Headers();
  headers.append("X-Early-Hint-Count-Start", "");
  await fetch(
    "https://example.com/browser/netwerk/test/browser/early_hint_pixel_count.sjs",
    { headers }
  );

  let requestUrl = `https://example.com/browser/netwerk/test/browser/early_hint_csp_options_html.sjs?as=${
    input.resource_type
  }&hinted=${input.hinted ? "1" : "0"}${input.csp ? "&csp=" + input.csp : ""}${
    input.csp_in_early_hint
      ? "&csp_in_early_hint=" + input.csp_in_early_hint
      : ""
  }${input.host ? "&host=" + input.host : ""}`;

  await BrowserTestUtils.openNewForegroundTab(gBrowser, requestUrl, true);

  let gotRequestCount = await fetch(
    "https://example.com/browser/netwerk/test/browser/early_hint_pixel_count.sjs"
  ).then(response => response.json());

  await Assert.deepEqual(gotRequestCount, expected_results, input.test_name);

  gBrowser.removeCurrentTab();
  Services.cache2.clear();
}