summaryrefslogtreecommitdiffstats
path: root/test/wpt/tests/fetch/metadata
diff options
context:
space:
mode:
Diffstat (limited to 'test/wpt/tests/fetch/metadata')
-rw-r--r--test/wpt/tests/fetch/metadata/META.yml4
-rw-r--r--test/wpt/tests/fetch/metadata/README.md9
-rw-r--r--test/wpt/tests/fetch/metadata/audio-worklet.https.html20
-rw-r--r--test/wpt/tests/fetch/metadata/embed.https.sub.tentative.html63
-rw-r--r--test/wpt/tests/fetch/metadata/fetch-preflight.https.sub.any.js29
-rw-r--r--test/wpt/tests/fetch/metadata/fetch.https.sub.any.js58
-rw-r--r--test/wpt/tests/fetch/metadata/generated/appcache-manifest.https.sub.html341
-rw-r--r--test/wpt/tests/fetch/metadata/generated/audioworklet.https.sub.html271
-rw-r--r--test/wpt/tests/fetch/metadata/generated/css-font-face.https.sub.tentative.html230
-rw-r--r--test/wpt/tests/fetch/metadata/generated/css-font-face.sub.tentative.html196
-rw-r--r--test/wpt/tests/fetch/metadata/generated/css-images.https.sub.tentative.html1384
-rw-r--r--test/wpt/tests/fetch/metadata/generated/css-images.sub.tentative.html1099
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-a.https.sub.html482
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-a.sub.html342
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-area.https.sub.html482
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-area.sub.html342
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-audio.https.sub.html325
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-audio.sub.html229
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-embed.https.sub.html224
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-embed.sub.html190
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-frame.https.sub.html309
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-frame.sub.html250
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-iframe.https.sub.html309
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-iframe.sub.html250
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-img-environment-change.https.sub.html357
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-img-environment-change.sub.html270
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-img.https.sub.html645
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-img.sub.html456
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-input-image.https.sub.html229
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-input-image.sub.html184
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-link-icon.https.sub.html371
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-link-icon.sub.html279
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-link-prefetch.https.optional.sub.html559
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-link-prefetch.optional.sub.html275
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-meta-refresh.https.optional.sub.html276
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-meta-refresh.optional.sub.html225
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-picture.https.sub.html997
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-picture.sub.html721
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-script.https.sub.html593
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-script.sub.html488
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-video-poster.https.sub.html243
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-video-poster.sub.html198
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-video.https.sub.html325
-rw-r--r--test/wpt/tests/fetch/metadata/generated/element-video.sub.html229
-rw-r--r--test/wpt/tests/fetch/metadata/generated/fetch-via-serviceworker.https.sub.html683
-rw-r--r--test/wpt/tests/fetch/metadata/generated/fetch.https.sub.html302
-rw-r--r--test/wpt/tests/fetch/metadata/generated/fetch.sub.html220
-rw-r--r--test/wpt/tests/fetch/metadata/generated/form-submission.https.sub.html522
-rw-r--r--test/wpt/tests/fetch/metadata/generated/form-submission.sub.html400
-rw-r--r--test/wpt/tests/fetch/metadata/generated/header-link.https.sub.html529
-rw-r--r--test/wpt/tests/fetch/metadata/generated/header-link.https.sub.tentative.html51
-rw-r--r--test/wpt/tests/fetch/metadata/generated/header-link.sub.html460
-rw-r--r--test/wpt/tests/fetch/metadata/generated/header-refresh.https.optional.sub.html273
-rw-r--r--test/wpt/tests/fetch/metadata/generated/header-refresh.optional.sub.html222
-rw-r--r--test/wpt/tests/fetch/metadata/generated/script-module-import-dynamic.https.sub.html254
-rw-r--r--test/wpt/tests/fetch/metadata/generated/script-module-import-dynamic.sub.html214
-rw-r--r--test/wpt/tests/fetch/metadata/generated/script-module-import-static.https.sub.html288
-rw-r--r--test/wpt/tests/fetch/metadata/generated/script-module-import-static.sub.html246
-rw-r--r--test/wpt/tests/fetch/metadata/generated/serviceworker.https.sub.html170
-rw-r--r--test/wpt/tests/fetch/metadata/generated/svg-image.https.sub.html367
-rw-r--r--test/wpt/tests/fetch/metadata/generated/svg-image.sub.html265
-rw-r--r--test/wpt/tests/fetch/metadata/generated/window-history.https.sub.html237
-rw-r--r--test/wpt/tests/fetch/metadata/generated/window-history.sub.html360
-rw-r--r--test/wpt/tests/fetch/metadata/generated/window-location.https.sub.html1184
-rw-r--r--test/wpt/tests/fetch/metadata/generated/window-location.sub.html894
-rw-r--r--test/wpt/tests/fetch/metadata/generated/worker-dedicated-constructor.https.sub.html118
-rw-r--r--test/wpt/tests/fetch/metadata/generated/worker-dedicated-constructor.sub.html204
-rw-r--r--test/wpt/tests/fetch/metadata/generated/worker-dedicated-importscripts.https.sub.html268
-rw-r--r--test/wpt/tests/fetch/metadata/generated/worker-dedicated-importscripts.sub.html228
-rw-r--r--test/wpt/tests/fetch/metadata/navigation.https.sub.html23
-rw-r--r--test/wpt/tests/fetch/metadata/object.https.sub.html62
-rw-r--r--test/wpt/tests/fetch/metadata/paint-worklet.https.html19
-rw-r--r--test/wpt/tests/fetch/metadata/portal.https.sub.html50
-rw-r--r--test/wpt/tests/fetch/metadata/preload.https.sub.html50
-rw-r--r--test/wpt/tests/fetch/metadata/redirect/multiple-redirect-https-downgrade-upgrade.sub.html18
-rw-r--r--test/wpt/tests/fetch/metadata/redirect/redirect-http-upgrade.sub.html17
-rw-r--r--test/wpt/tests/fetch/metadata/redirect/redirect-https-downgrade.sub.html17
-rw-r--r--test/wpt/tests/fetch/metadata/report.https.sub.html33
-rw-r--r--test/wpt/tests/fetch/metadata/report.https.sub.html.sub.headers3
-rw-r--r--test/wpt/tests/fetch/metadata/resources/appcache-iframe.sub.html15
-rw-r--r--test/wpt/tests/fetch/metadata/resources/dedicatedWorker.js1
-rw-r--r--test/wpt/tests/fetch/metadata/resources/echo-as-json.py29
-rw-r--r--test/wpt/tests/fetch/metadata/resources/echo-as-script.py14
-rw-r--r--test/wpt/tests/fetch/metadata/resources/es-module.sub.js1
-rw-r--r--test/wpt/tests/fetch/metadata/resources/fetch-via-serviceworker--fallback--sw.js3
-rw-r--r--test/wpt/tests/fetch/metadata/resources/fetch-via-serviceworker--respondWith--sw.js3
-rw-r--r--test/wpt/tests/fetch/metadata/resources/fetch-via-serviceworker-frame.html3
-rw-r--r--test/wpt/tests/fetch/metadata/resources/header-link.py15
-rw-r--r--test/wpt/tests/fetch/metadata/resources/helper.js42
-rw-r--r--test/wpt/tests/fetch/metadata/resources/helper.sub.js67
-rw-r--r--test/wpt/tests/fetch/metadata/resources/message-opener.html17
-rw-r--r--test/wpt/tests/fetch/metadata/resources/post-to-owner.py36
-rw-r--r--test/wpt/tests/fetch/metadata/resources/record-header.py145
-rw-r--r--test/wpt/tests/fetch/metadata/resources/record-headers.py73
-rw-r--r--test/wpt/tests/fetch/metadata/resources/redirectTestHelper.sub.js167
-rw-r--r--test/wpt/tests/fetch/metadata/resources/serviceworker-accessors-frame.html3
-rw-r--r--test/wpt/tests/fetch/metadata/resources/serviceworker-accessors.sw.js14
-rw-r--r--test/wpt/tests/fetch/metadata/resources/sharedWorker.js9
-rw-r--r--test/wpt/tests/fetch/metadata/resources/unload-with-beacon.html12
-rw-r--r--test/wpt/tests/fetch/metadata/resources/xslt-test.sub.xml12
-rw-r--r--test/wpt/tests/fetch/metadata/serviceworker-accessors.https.sub.html51
-rw-r--r--test/wpt/tests/fetch/metadata/sharedworker.https.sub.html40
-rw-r--r--test/wpt/tests/fetch/metadata/style.https.sub.html86
-rw-r--r--test/wpt/tests/fetch/metadata/tools/README.md126
-rw-r--r--test/wpt/tests/fetch/metadata/tools/fetch-metadata.conf.yml806
-rw-r--r--test/wpt/tests/fetch/metadata/tools/generate.py195
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/appcache-manifest.sub.https.html63
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/audioworklet.https.sub.html53
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/css-font-face.sub.html60
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/css-images.sub.html137
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-a.sub.html72
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-area.sub.html72
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-audio.sub.html51
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-embed.sub.html54
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-frame.sub.html62
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-iframe.sub.html62
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-img-environment-change.sub.html78
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-img.sub.html52
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-input-image.sub.html48
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-link-icon.sub.html75
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-link-prefetch.optional.sub.html71
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-meta-refresh.optional.sub.html60
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-picture.sub.html101
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-script.sub.html54
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-video-poster.sub.html62
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/element-video.sub.html51
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/fetch-via-serviceworker.https.sub.html88
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/fetch.sub.html42
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/form-submission.sub.html87
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/header-link.sub.html56
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/header-refresh.optional.sub.html59
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/script-module-import-dynamic.sub.html35
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/script-module-import-static.sub.html53
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/serviceworker.https.sub.html72
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/svg-image.sub.html75
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/window-history.sub.html134
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/window-location.sub.html128
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/worker-dedicated-constructor.sub.html49
-rw-r--r--test/wpt/tests/fetch/metadata/tools/templates/worker-dedicated-importscripts.sub.html54
-rw-r--r--test/wpt/tests/fetch/metadata/track.https.sub.html119
-rw-r--r--test/wpt/tests/fetch/metadata/trailing-dot.https.sub.any.js30
-rw-r--r--test/wpt/tests/fetch/metadata/unload.https.sub.html64
-rw-r--r--test/wpt/tests/fetch/metadata/window-open.https.sub.html199
-rw-r--r--test/wpt/tests/fetch/metadata/worker.https.sub.html24
-rw-r--r--test/wpt/tests/fetch/metadata/xslt.https.sub.html25
145 files changed, 29325 insertions, 0 deletions
diff --git a/test/wpt/tests/fetch/metadata/META.yml b/test/wpt/tests/fetch/metadata/META.yml
new file mode 100644
index 0000000..85f0a7d
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/META.yml
@@ -0,0 +1,4 @@
+spec: https://w3c.github.io/webappsec-fetch-metadata/
+suggested_reviewers:
+ - mikewest
+ - iVanlIsh
diff --git a/test/wpt/tests/fetch/metadata/README.md b/test/wpt/tests/fetch/metadata/README.md
new file mode 100644
index 0000000..34864d4
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/README.md
@@ -0,0 +1,9 @@
+Fetch Metadata Tests
+====================
+
+This directory contains tests related to the Fetch Metadata proposal:
+
+: Explainer
+:: <https://github.com/w3c/webappsec-fetch-metadata>
+: "Spec"
+:: <https://w3c.github.io/webappsec-fetch-metadata/>
diff --git a/test/wpt/tests/fetch/metadata/audio-worklet.https.html b/test/wpt/tests/fetch/metadata/audio-worklet.https.html
new file mode 100644
index 0000000..3b768ef
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/audio-worklet.https.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<script>
+
+ promise_test(async t => {
+ const nonce = token();
+ const key = "worklet-destination" + nonce;
+ const context = new AudioContext();
+
+ await context.audioWorklet.addModule("/fetch/metadata/resources/record-header.py?file=" + key);
+ const expected = {"site": "same-origin", "user": "", "mode": "cors", "dest": "audioworklet"};
+ await validate_expectations(key, expected);
+ }, "The fetch metadata for audio worklet");
+
+</script>
+<body></body>
diff --git a/test/wpt/tests/fetch/metadata/embed.https.sub.tentative.html b/test/wpt/tests/fetch/metadata/embed.https.sub.tentative.html
new file mode 100644
index 0000000..1900dbd
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/embed.https.sub.tentative.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<link rel="author" href="mtrzos@google.com" title="Maciek Trzos">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<body>
+
+<p>Relevant issue: <a href="https://github.com/whatwg/html/issues/513">
+&lt;embed> should support loading random HTML documents, like &lt;object>
+</a></p>
+
+<script>
+ const nonce = token();
+
+ const origins = {
+ "same-origin": "https://{{host}}:{{ports[https][0]}}",
+ "same-site": "https://{{hosts[][www]}}:{{ports[https][0]}}",
+ "cross-site": "https://{{hosts[alt][www]}}:{{ports[https][0]}}",
+ };
+
+ for (let site in origins) {
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = "embed-" + site + "-" + nonce;
+
+ let el = document.createElement('embed');
+ el.src = origins[site] + "/fetch/metadata/resources/record-header.py?file=" + key;
+ el.onload = _ => {
+ let expected = {"dest": "embed", "site": site, "user": "", "mode": "navigate"};
+ validate_expectations(key, expected, site + " embed")
+ .then(resolve)
+ .catch(reject);
+ };
+
+ document.body.appendChild(el);
+ })
+ }, "Wrapper: " + site + " embed");
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = "post-embed-" + site + "-" + nonce;
+
+ let el = document.createElement('embed');
+ el.src = "/common/blank.html";
+ el.addEventListener("load", _ => {
+ el.addEventListener("load", _ => {
+ let expected = {"dest": "embed", "site": site, "user":"", "mode":"navigate"};
+ validate_expectations(key, expected, "Navigate to " + site + " embed")
+ .then(resolve)
+ .catch(reject);
+ }, { once: true });
+
+ // Navigate the existing `<embed>`
+ window.frames[window.length - 1].location = origins[site] + "/fetch/metadata/resources/record-header.py?file=" + key;
+ }, { once: true });
+
+ document.body.appendChild(el);
+ })
+ }, "Wrapper: Navigate to " + site + " embed");
+ }
+</script>
diff --git a/test/wpt/tests/fetch/metadata/fetch-preflight.https.sub.any.js b/test/wpt/tests/fetch/metadata/fetch-preflight.https.sub.any.js
new file mode 100644
index 0000000..d524743
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/fetch-preflight.https.sub.any.js
@@ -0,0 +1,29 @@
+// META: global=window,worker
+// META: script=/fetch/metadata/resources/helper.js
+
+// Site
+promise_test(t => {
+ return validate_expectations_custom_url("https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/metadata/resources/echo-as-json.py",
+ {
+ mode: "cors",
+ headers: { 'x-test': 'testing' }
+ }, {
+ "site": "same-site",
+ "user": "",
+ "mode": "cors",
+ "dest": "empty"
+ }, "Same-site fetch with preflight");
+}, "Same-site fetch with preflight");
+
+promise_test(t => {
+ return validate_expectations_custom_url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/metadata/resources/echo-as-json.py",
+ {
+ mode: "cors",
+ headers: { 'x-test': 'testing' }
+ }, {
+ "site": "cross-site",
+ "user": "",
+ "mode": "cors",
+ "dest": "empty"
+ }, "Cross-site fetch with preflight");
+}, "Cross-site fetch with preflight");
diff --git a/test/wpt/tests/fetch/metadata/fetch.https.sub.any.js b/test/wpt/tests/fetch/metadata/fetch.https.sub.any.js
new file mode 100644
index 0000000..aeec5cd
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/fetch.https.sub.any.js
@@ -0,0 +1,58 @@
+// META: global=window,worker
+// META: script=/fetch/metadata/resources/helper.js
+
+// Site
+promise_test(t => {
+ return validate_expectations_custom_url("https://{{host}}:{{ports[https][0]}}/fetch/metadata/resources/echo-as-json.py", {}, {
+ "site": "same-origin",
+ "user": "",
+ "mode": "cors",
+ "dest": "empty"
+ }, "Same-origin fetch");
+}, "Same-origin fetch");
+
+promise_test(t => {
+ return validate_expectations_custom_url("https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/metadata/resources/echo-as-json.py", {}, {
+ "site": "same-site",
+ "user": "",
+ "mode": "cors",
+ "dest": "empty"
+ }, "Same-site fetch");
+}, "Same-site fetch");
+
+promise_test(t => {
+ return validate_expectations_custom_url("https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/metadata/resources/echo-as-json.py", {}, {
+ "site": "cross-site",
+ "user": "",
+ "mode": "cors",
+ "dest": "empty"
+ }, "Cross-site fetch");
+}, "Cross-site fetch");
+
+// Mode
+promise_test(t => {
+ return validate_expectations_custom_url("https://{{host}}:{{ports[https][0]}}/fetch/metadata/resources/echo-as-json.py", {mode: "same-origin"}, {
+ "site": "same-origin",
+ "user": "",
+ "mode": "same-origin",
+ "dest": "empty"
+ }, "Same-origin mode");
+}, "Same-origin mode");
+
+promise_test(t => {
+ return validate_expectations_custom_url("https://{{host}}:{{ports[https][0]}}/fetch/metadata/resources/echo-as-json.py", {mode: "cors"}, {
+ "site": "same-origin",
+ "user": "",
+ "mode": "cors",
+ "dest": "empty"
+ }, "CORS mode");
+}, "CORS mode");
+
+promise_test(t => {
+ return validate_expectations_custom_url("https://{{host}}:{{ports[https][0]}}/fetch/metadata/resources/echo-as-json.py", {mode: "no-cors"}, {
+ "site": "same-origin",
+ "user": "",
+ "mode": "no-cors",
+ "dest": "empty"
+ }, "no-CORS mode");
+}, "no-CORS mode");
diff --git a/test/wpt/tests/fetch/metadata/generated/appcache-manifest.https.sub.html b/test/wpt/tests/fetch/metadata/generated/appcache-manifest.https.sub.html
new file mode 100644
index 0000000..cf322fd
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/appcache-manifest.https.sub.html
@@ -0,0 +1,341 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/appcache-manifest.sub.https.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for Appcache manifest</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url) {
+ const iframe = document.createElement('iframe');
+ iframe.src =
+ '/fetch/metadata/resources/appcache-iframe.sub.html?manifest=' + encodeURIComponent(url);
+
+ return new Promise((resolve) => {
+ addEventListener('message', function onMessage(event) {
+ if (event.source !== iframe.contentWindow) {
+ return;
+ }
+ removeEventListener('message', onMessage);
+ resolve(event.data);
+ });
+
+ document.body.appendChild(iframe);
+ })
+ .then((message) => {
+ if (message !== 'okay') {
+ throw message;
+ }
+ })
+ .then(() => iframe.remove());
+ }
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - Same origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsCrossSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - Cross-site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsSameSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - Same site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpOrigin', 'httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - HTTPS upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - Cross-Site -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - Cross-Site -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - Same-Origin -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - Same-Origin -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - Same-Site -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - Same-Site -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-site - Same-Site -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, []))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-mode');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, []))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['empty']);
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-dest');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, []))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, 'sec-fetch-user');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/audioworklet.https.sub.html b/test/wpt/tests/fetch/metadata/generated/audioworklet.https.sub.html
new file mode 100644
index 0000000..64fb760
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/audioworklet.https.sub.html
@@ -0,0 +1,271 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/audioworklet.https.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for AudioWorklet module</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test) {
+ return test_driver.bless(
+ 'Enable WebAudio playback',
+ () => {
+ const audioContext = new AudioContext();
+
+ test.add_cleanup(() => audioContext.close());
+
+ return audioContext.audioWorklet.addModule(url);
+ }
+ );
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin'], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite'], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite'], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['audioworklet']);
+ });
+ }, 'sec-fetch-dest');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/css-font-face.https.sub.tentative.html b/test/wpt/tests/fetch/metadata/generated/css-font-face.https.sub.tentative.html
new file mode 100644
index 0000000..332effe
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/css-font-face.https.sub.tentative.html
@@ -0,0 +1,230 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/css-font-face.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for CSS font-face</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ let count = 0;
+
+ function induceRequest(t, url) {
+ const id = `el-${count += 1}`;
+ const style = document.createElement('style');
+ style.appendChild(document.createTextNode(`
+ @font-face {
+ font-family: wpt-font-family${id};
+ src: url(${url});
+ }
+ #el-${id} {
+ font-family: wpt-font-family${id};
+ }
+ `));
+ const div = document.createElement('div');
+ div.setAttribute('id', 'el-' + id);
+ div.appendChild(style);
+ div.appendChild(document.createTextNode('x'));
+ document.body.appendChild(div);
+
+ t.add_cleanup(() => div.remove());
+
+ return document.fonts.ready;
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsCrossSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsSameSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, []))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, []))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['font']);
+ });
+ }, 'sec-fetch-dest');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, []))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/css-font-face.sub.tentative.html b/test/wpt/tests/fetch/metadata/generated/css-font-face.sub.tentative.html
new file mode 100644
index 0000000..8a0b90c
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/css-font-face.sub.tentative.html
@@ -0,0 +1,196 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/css-font-face.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for CSS font-face</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ let count = 0;
+
+ function induceRequest(t, url) {
+ const id = `el-${count += 1}`;
+ const style = document.createElement('style');
+ style.appendChild(document.createTextNode(`
+ @font-face {
+ font-family: wpt-font-family${id};
+ src: url(${url});
+ }
+ #el-${id} {
+ font-family: wpt-font-family${id};
+ }
+ `));
+ const div = document.createElement('div');
+ div.setAttribute('id', 'el-' + id);
+ div.appendChild(style);
+ div.appendChild(document.createTextNode('x'));
+ document.body.appendChild(div);
+
+ t.add_cleanup(() => div.remove());
+
+ return document.fonts.ready;
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpSameSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpCrossSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpSameSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpCrossSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpSameSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpCrossSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpSameSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpCrossSite']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin', 'httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/css-images.https.sub.tentative.html b/test/wpt/tests/fetch/metadata/generated/css-images.https.sub.tentative.html
new file mode 100644
index 0000000..3fa2401
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/css-images.https.sub.tentative.html
@@ -0,0 +1,1384 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/css-images.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for CSS image-accepting properties</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ /**
+ * The subtests in this file use an iframe to induce requests for CSS
+ * resources because an iframe's `onload` event is the most direct and
+ * generic mechanism to detect loading of CSS resources. As an optimization,
+ * the subtests share the same iframe and document.
+ */
+ const declarations = [];
+ const iframe = document.createElement('iframe');
+ const whenIframeReady = new Promise((resolve, reject) => {
+ iframe.onload = resolve;
+ iframe.onerror = reject;
+ });
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'same-origin');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Same origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Same origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Same origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Same origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Same origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'cross-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Cross-site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Cross-site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Cross-site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Cross-site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Cross-site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'same-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Same site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Same site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Same site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Same site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Same site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'cross-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'same-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'cross-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Cross-Site -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Cross-Site -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Cross-Site -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Cross-Site -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Cross-Site -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'cross-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Cross-Site -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Cross-Site -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Cross-Site -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Cross-Site -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Cross-Site -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'cross-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Cross-Site -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Cross-Site -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Cross-Site -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Cross-Site -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Cross-Site -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'same-origin');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Same-Origin -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Same-Origin -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Same-Origin -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Same-Origin -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Same-Origin -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'same-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Same-Origin -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Same-Origin -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Same-Origin -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Same-Origin -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Same-Origin -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'cross-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Same-Origin -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Same-Origin -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Same-Origin -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Same-Origin -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Same-Origin -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'same-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Same-Site -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Same-Site -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Same-Site -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Same-Site -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Same-Site -> Same Origin');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'same-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Same-Site -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Same-Site -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Same-Site -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Same-Site -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Same-Site -> Same-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'cross-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Same-Site -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Same-Site -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Same-Site -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Same-Site -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Same-Site -> Cross-Site');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'cross-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - HTTPS downgrade-upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - HTTPS downgrade-upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - HTTPS downgrade-upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - HTTPS downgrade-upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - HTTPS downgrade-upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_equals(headers['sec-fetch-mode'], 'no-cors');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-mode');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-mode');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-mode');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-mode');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-mode');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_equals(headers['sec-fetch-dest'], 'image');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-dest');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['image']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-dest');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['image']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-dest');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['image']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-dest');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['image']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-dest');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-user');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-user');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-user');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-user');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, []);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-user');
+
+ iframe.srcdoc = declarations.map((declaration, index) => `
+ <style>.el${index} { ${declaration} }</style><div class="el${index}"></div>`
+ ).join('');
+ document.body.appendChild(iframe);
+
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/css-images.sub.tentative.html b/test/wpt/tests/fetch/metadata/generated/css-images.sub.tentative.html
new file mode 100644
index 0000000..f1ef27c
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/css-images.sub.tentative.html
@@ -0,0 +1,1099 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/css-images.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for CSS image-accepting properties</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ /**
+ * The subtests in this file use an iframe to induce requests for CSS
+ * resources because an iframe's `onload` event is the most direct and
+ * generic mechanism to detect loading of CSS resources. As an optimization,
+ * the subtests share the same iframe and document.
+ */
+ const declarations = [];
+ const iframe = document.createElement('iframe');
+ const whenIframeReady = new Promise((resolve, reject) => {
+ iframe.onload = resolve;
+ iframe.onerror = reject;
+ });
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'cross-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - HTTPS upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - HTTPS upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - HTTPS upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - HTTPS upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - HTTPS upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_equals(headers['sec-fetch-site'], 'cross-site');
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image sec-fetch-site - HTTPS downgrade-upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image sec-fetch-site - HTTPS downgrade-upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content sec-fetch-site - HTTPS downgrade-upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor sec-fetch-site - HTTPS downgrade-upgrade');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image sec-fetch-site - HTTPS downgrade-upgrade');
+
+ iframe.srcdoc = declarations.map((declaration, index) => `
+ <style>.el${index} { ${declaration} }</style><div class="el${index}"></div>`
+ ).join('');
+ document.body.appendChild(iframe);
+
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-a.https.sub.html b/test/wpt/tests/fetch/metadata/generated/element-a.https.sub.html
new file mode 100644
index 0000000..dffd36c
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-a.https.sub.html
@@ -0,0 +1,482 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-a.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for HTML "a" element navigation</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, {test, userActivated, attributes}) {
+ const win = window.open();
+ const anchor = win.document.createElement('a');
+ anchor.setAttribute('href', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ anchor.setAttribute(name, value);
+ }
+
+ win.document.body.appendChild(anchor);
+
+ test.add_cleanup(() => win.close());
+
+ if (userActivated) {
+ test_driver.bless('enable user activation', () => anchor.click());
+ } else {
+ anchor.click();
+ }
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['navigate']);
+ });
+ }, 'sec-fetch-mode - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {"download": ""}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - attributes: download');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['document']);
+ });
+ }, 'sec-fetch-dest - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {"download": ""}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['empty']);
+ });
+ }, 'sec-fetch-dest - attributes: download');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: true,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-user');
+ assert_array_equals(headers['sec-fetch-user'], ['?1']);
+ });
+ }, 'sec-fetch-user - no attributes with user activation');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-a.sub.html b/test/wpt/tests/fetch/metadata/generated/element-a.sub.html
new file mode 100644
index 0000000..0661de3
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-a.sub.html
@@ -0,0 +1,342 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-a.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for HTML "a" element navigation</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, {test, userActivated, attributes}) {
+ const win = window.open();
+ const anchor = win.document.createElement('a');
+ anchor.setAttribute('href', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ anchor.setAttribute(name, value);
+ }
+
+ win.document.body.appendChild(anchor);
+
+ test.add_cleanup(() => win.close());
+
+ if (userActivated) {
+ test_driver.bless('enable user activation', () => anchor.click());
+ } else {
+ anchor.click();
+ }
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent) - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-area.https.sub.html b/test/wpt/tests/fetch/metadata/generated/element-area.https.sub.html
new file mode 100644
index 0000000..be3f5f9
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-area.https.sub.html
@@ -0,0 +1,482 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-area.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for HTML "area" element navigation</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, {test, userActivated, attributes}) {
+ const win = window.open();
+ const area = win.document.createElement('area');
+ area.setAttribute('href', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ area.setAttribute(name, value);
+ }
+
+ win.document.body.appendChild(area);
+
+ test.add_cleanup(() => win.close());
+
+ if (userActivated) {
+ test_driver.bless('enable user activation', () => area.click());
+ } else {
+ area.click();
+ }
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['navigate']);
+ });
+ }, 'sec-fetch-mode - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {"download": ""}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - attributes: download');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['document']);
+ });
+ }, 'sec-fetch-dest - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {"download": ""}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['empty']);
+ });
+ }, 'sec-fetch-dest - attributes: download');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: true,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-user');
+ assert_array_equals(headers['sec-fetch-user'], ['?1']);
+ });
+ }, 'sec-fetch-user - no attributes with user activation');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-area.sub.html b/test/wpt/tests/fetch/metadata/generated/element-area.sub.html
new file mode 100644
index 0000000..5f5c338
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-area.sub.html
@@ -0,0 +1,342 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-area.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for HTML "area" element navigation</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, {test, userActivated, attributes}) {
+ const win = window.open();
+ const area = win.document.createElement('area');
+ area.setAttribute('href', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ area.setAttribute(name, value);
+ }
+
+ win.document.body.appendChild(area);
+
+ test.add_cleanup(() => win.close());
+
+ if (userActivated) {
+ test_driver.bless('enable user activation', () => area.click());
+ } else {
+ area.click();
+ }
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent) - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: false,
+ attributes: {}
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-audio.https.sub.html b/test/wpt/tests/fetch/metadata/generated/element-audio.https.sub.html
new file mode 100644
index 0000000..a9d9512
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-audio.https.sub.html
@@ -0,0 +1,325 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-audio.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "audio" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, attributes) {
+ const audio = document.createElement('audio');
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ audio.setAttribute(name, value);
+ }
+
+ return new Promise((resolve) => {
+ audio.setAttribute('src', url);
+ audio.onload = audio.onerror = resolve;
+ });
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {"crossorigin": ""}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - attributes: crossorigin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {"crossorigin": "anonymous"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - attributes: crossorigin=anonymous');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {"crossorigin": "use-credentials"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - attributes: crossorigin=use-credentials');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['audio']);
+ });
+ }, 'sec-fetch-dest - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-audio.sub.html b/test/wpt/tests/fetch/metadata/generated/element-audio.sub.html
new file mode 100644
index 0000000..2b62632
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-audio.sub.html
@@ -0,0 +1,229 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-audio.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "audio" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, attributes) {
+ const audio = document.createElement('audio');
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ audio.setAttribute(name, value);
+ }
+
+ return new Promise((resolve) => {
+ audio.setAttribute('src', url);
+ audio.onload = audio.onerror = resolve;
+ });
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent), no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade, no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-embed.https.sub.html b/test/wpt/tests/fetch/metadata/generated/element-embed.https.sub.html
new file mode 100644
index 0000000..819bed8
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-embed.https.sub.html
@@ -0,0 +1,224 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-embed.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "embed" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ function induceRequest(t, url) {
+ const embed = document.createElement('embed');
+ embed.setAttribute('src', url);
+ document.body.appendChild(embed);
+
+ t.add_cleanup(() => embed.remove());
+
+ return new Promise((resolve) => embed.addEventListener('load', resolve));
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, [], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, [], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['embed']);
+ });
+ }, 'sec-fetch-dest');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, [], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-embed.sub.html b/test/wpt/tests/fetch/metadata/generated/element-embed.sub.html
new file mode 100644
index 0000000..b6e14a5
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-embed.sub.html
@@ -0,0 +1,190 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-embed.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "embed" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ function induceRequest(t, url) {
+ const embed = document.createElement('embed');
+ embed.setAttribute('src', url);
+ document.body.appendChild(embed);
+
+ t.add_cleanup(() => embed.remove());
+
+ return new Promise((resolve) => embed.addEventListener('load', resolve));
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-frame.https.sub.html b/test/wpt/tests/fetch/metadata/generated/element-frame.https.sub.html
new file mode 100644
index 0000000..17504ff
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-frame.https.sub.html
@@ -0,0 +1,309 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-frame.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "frame" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test, userActivated) {
+ const frame = document.createElement('frame');
+
+ const setSrc = () => frame.setAttribute('src', url);
+
+ document.body.appendChild(frame);
+ test.add_cleanup(() => frame.remove());
+
+ return new Promise((resolve) => {
+ if (userActivated) {
+ test_driver.bless('enable user activation', setSrc);
+ } else {
+ setSrc();
+ }
+
+ frame.onload = frame.onerror = resolve;
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['navigate']);
+ });
+ }, 'sec-fetch-mode');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['frame']);
+ });
+ }, 'sec-fetch-dest');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ t,
+ true
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-user');
+ assert_array_equals(headers['sec-fetch-user'], ['?1']);
+ });
+ }, 'sec-fetch-user with user activation');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-frame.sub.html b/test/wpt/tests/fetch/metadata/generated/element-frame.sub.html
new file mode 100644
index 0000000..2d9a7ec
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-frame.sub.html
@@ -0,0 +1,250 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-frame.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "frame" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test, userActivated) {
+ const frame = document.createElement('frame');
+
+ const setSrc = () => frame.setAttribute('src', url);
+
+ document.body.appendChild(frame);
+ test.add_cleanup(() => frame.remove());
+
+ return new Promise((resolve) => {
+ if (userActivated) {
+ test_driver.bless('enable user activation', setSrc);
+ } else {
+ setSrc();
+ }
+
+ frame.onload = frame.onerror = resolve;
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-iframe.https.sub.html b/test/wpt/tests/fetch/metadata/generated/element-iframe.https.sub.html
new file mode 100644
index 0000000..fba1c8b
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-iframe.https.sub.html
@@ -0,0 +1,309 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-iframe.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "frame" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test, userActivated) {
+ const iframe = document.createElement('iframe');
+
+ const setSrc = () => iframe.setAttribute('src', url);
+
+ document.body.appendChild(iframe);
+ test.add_cleanup(() => iframe.remove());
+
+ return new Promise((resolve) => {
+ if (userActivated) {
+ test_driver.bless('enable user activation', setSrc);
+ } else {
+ setSrc();
+ }
+
+ iframe.onload = iframe.onerror = resolve;
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['navigate']);
+ });
+ }, 'sec-fetch-mode');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['iframe']);
+ });
+ }, 'sec-fetch-dest');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ t,
+ true
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-user');
+ assert_array_equals(headers['sec-fetch-user'], ['?1']);
+ });
+ }, 'sec-fetch-user with user activation');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-iframe.sub.html b/test/wpt/tests/fetch/metadata/generated/element-iframe.sub.html
new file mode 100644
index 0000000..6f71cc0
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-iframe.sub.html
@@ -0,0 +1,250 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-iframe.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "frame" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test, userActivated) {
+ const iframe = document.createElement('iframe');
+
+ const setSrc = () => iframe.setAttribute('src', url);
+
+ document.body.appendChild(iframe);
+ test.add_cleanup(() => iframe.remove());
+
+ return new Promise((resolve) => {
+ if (userActivated) {
+ test_driver.bless('enable user activation', setSrc);
+ } else {
+ setSrc();
+ }
+
+ iframe.onload = iframe.onerror = resolve;
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ t,
+ false
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-img-environment-change.https.sub.html b/test/wpt/tests/fetch/metadata/generated/element-img-environment-change.https.sub.html
new file mode 100644
index 0000000..a19aa11
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-img-environment-change.https.sub.html
@@ -0,0 +1,357 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-img-environment-change.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on image request triggered by change to environment</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ // The response to the request under test must describe a valid image
+ // resource in order for the `load` event to be fired.
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ function induceRequest(t, url, attributes) {
+ const iframe = document.createElement('iframe');
+ iframe.style.width = '50px';
+ document.body.appendChild(iframe);
+ t.add_cleanup(() => iframe.remove());
+ iframe.contentDocument.open();
+ iframe.contentDocument.close();
+
+ const image = iframe.contentDocument.createElement('img');
+ for (const [ name, value ] of Object.entries(attributes)) {
+ image.setAttribute(name, value);
+ }
+ iframe.contentDocument.body.appendChild(image);
+
+ image.setAttribute('srcset', `${url} 100w, /media/1x1-green.png 1w`);
+ image.setAttribute('sizes', '(max-width: 100px) 1px, (min-width: 150px) 123px');
+
+ return new Promise((resolve) => {
+ image.onload = image.onerror = resolve;
+ })
+ .then(() => {
+
+ iframe.style.width = '200px';
+
+ return new Promise((resolve) => image.onload = resolve);
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {"crossorigin": ""}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - attributes: crossorigin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {"crossorigin": "anonymous"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - attributes: crossorigin=anonymous');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {"crossorigin": "use-credentials"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - attributes: crossorigin=use-credentials');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['image']);
+ });
+ }, 'sec-fetch-dest - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-img-environment-change.sub.html b/test/wpt/tests/fetch/metadata/generated/element-img-environment-change.sub.html
new file mode 100644
index 0000000..9665872
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-img-environment-change.sub.html
@@ -0,0 +1,270 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-img-environment-change.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on image request triggered by change to environment</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ // The response to the request under test must describe a valid image
+ // resource in order for the `load` event to be fired.
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ function induceRequest(t, url, attributes) {
+ const iframe = document.createElement('iframe');
+ iframe.style.width = '50px';
+ document.body.appendChild(iframe);
+ t.add_cleanup(() => iframe.remove());
+ iframe.contentDocument.open();
+ iframe.contentDocument.close();
+
+ const image = iframe.contentDocument.createElement('img');
+ for (const [ name, value ] of Object.entries(attributes)) {
+ image.setAttribute(name, value);
+ }
+ iframe.contentDocument.body.appendChild(image);
+
+ image.setAttribute('srcset', `${url} 100w, /media/1x1-green.png 1w`);
+ image.setAttribute('sizes', '(max-width: 100px) 1px, (min-width: 150px) 123px');
+
+ return new Promise((resolve) => {
+ image.onload = image.onerror = resolve;
+ })
+ .then(() => {
+
+ iframe.style.width = '200px';
+
+ return new Promise((resolve) => image.onload = resolve);
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent), no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade, no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-img.https.sub.html b/test/wpt/tests/fetch/metadata/generated/element-img.https.sub.html
new file mode 100644
index 0000000..51d6e08
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-img.https.sub.html
@@ -0,0 +1,645 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-img.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "img" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, sourceAttr, attributes) {
+ const image = document.createElement('img');
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ image.setAttribute(name, value);
+ }
+
+ return new Promise((resolve) => {
+ image.setAttribute(sourceAttr, url);
+ image.onload = image.onerror = resolve;
+ });
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - src - Same origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - srcset - Same origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - src - Cross-site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - srcset - Cross-site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - src - Same site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - srcset - Same site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - src - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - srcset - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - src - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - srcset - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - src - Cross-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - srcset - Cross-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - src - Cross-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - srcset - Cross-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - src - Cross-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - srcset - Cross-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - src - Same-Origin -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - srcset - Same-Origin -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - src - Same-Origin -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - srcset - Same-Origin -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - src - Same-Origin -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - srcset - Same-Origin -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - src - Same-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - srcset - Same-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - src - Same-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - srcset - Same-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - src - Same-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - srcset - Same-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - src - HTTPS downgrade-upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - src - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'src',
+ {"crossorigin": ""}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - src - attributes: crossorigin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'src',
+ {"crossorigin": "anonymous"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - src - attributes: crossorigin=anonymous');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'src',
+ {"crossorigin": "use-credentials"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - src - attributes: crossorigin=use-credentials');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - srcset - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'srcset',
+ {"crossorigin": ""}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - srcset - attributes: crossorigin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'srcset',
+ {"crossorigin": "anonymous"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - srcset - attributes: crossorigin=anonymous');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'srcset',
+ {"crossorigin": "use-credentials"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - srcset - attributes: crossorigin=use-credentials');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['image']);
+ });
+ }, 'sec-fetch-dest - src - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['image']);
+ });
+ }, 'sec-fetch-dest - srcset - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - src - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - srcset - no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-img.sub.html b/test/wpt/tests/fetch/metadata/generated/element-img.sub.html
new file mode 100644
index 0000000..5a4b152
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-img.sub.html
@@ -0,0 +1,456 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-img.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "img" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, sourceAttr, attributes) {
+ const image = document.createElement('img');
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ image.setAttribute(name, value);
+ }
+
+ return new Promise((resolve) => {
+ image.setAttribute(sourceAttr, url);
+ image.onload = image.onerror = resolve;
+ });
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - src - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - srcset - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - src - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - srcset - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - src - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - srcset - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - src - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - srcset - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - src - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - srcset - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - src - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - srcset - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - src - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - srcset - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - src - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - srcset - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - src - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - srcset - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - src - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - srcset - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - src - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - srcset - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - src - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - srcset - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - src - HTTPS downgrade (header not sent), no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - srcset - HTTPS downgrade (header not sent), no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - src - HTTPS upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - srcset - HTTPS upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']),
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - src - HTTPS downgrade-upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']),
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - srcset - HTTPS downgrade-upgrade, no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-input-image.https.sub.html b/test/wpt/tests/fetch/metadata/generated/element-input-image.https.sub.html
new file mode 100644
index 0000000..7fa6740
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-input-image.https.sub.html
@@ -0,0 +1,229 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-input-image.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "input" element with type="button"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test) {
+ const input = document.createElement('input');
+ input.setAttribute('type', 'image');
+
+ document.body.appendChild(input);
+ test.add_cleanup(() => input.remove());
+
+ return new Promise((resolve) => {
+ input.onload = input.onerror = resolve;
+ input.setAttribute('src', url);
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsOrigin']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsCrossSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsSameSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, []), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, []), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['image']);
+ });
+ }, 'sec-fetch-dest - no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, []), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-input-image.sub.html b/test/wpt/tests/fetch/metadata/generated/element-input-image.sub.html
new file mode 100644
index 0000000..fb2a146
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-input-image.sub.html
@@ -0,0 +1,184 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-input-image.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "input" element with type="button"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test) {
+ const input = document.createElement('input');
+ input.setAttribute('type', 'image');
+
+ document.body.appendChild(input);
+ test.add_cleanup(() => input.remove());
+
+ return new Promise((resolve) => {
+ input.onload = input.onerror = resolve;
+ input.setAttribute('src', url);
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpOrigin']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpSameSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpCrossSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpOrigin']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpSameSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpCrossSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpOrigin']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpSameSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpCrossSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpOrigin']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpSameSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpCrossSite']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpOrigin']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent), no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpOrigin', 'httpsOrigin']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade, no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade, no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-link-icon.https.sub.html b/test/wpt/tests/fetch/metadata/generated/element-link-icon.https.sub.html
new file mode 100644
index 0000000..b244960
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-link-icon.https.sub.html
@@ -0,0 +1,371 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-link-icon.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for HTML "link" element with rel="icon"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ /**
+ * The `link` element supports a `load` event. That event would reliably
+ * indicate that the browser had received the request. Multiple major
+ * browsers do not implement the event, however, so in order to promote the
+ * visibility of this test, a less efficient polling-based detection
+ * mechanism is used.
+ *
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1638188
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=1083034
+ */
+ function induceRequest(t, url, attributes) {
+ const link = document.createElement('link');
+ link.setAttribute('rel', 'icon');
+ link.setAttribute('href', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ link.setAttribute(name, value);
+ }
+
+ document.head.appendChild(link);
+ t.add_cleanup(() => link.remove());
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {"crossorigin": ""}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode attributes: crossorigin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {"crossorigin": "anonymous"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode attributes: crossorigin=anonymous');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {"crossorigin": "use-credentials"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode attributes: crossorigin=use-credentials');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['empty']);
+ });
+ }, 'sec-fetch-dest no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-link-icon.sub.html b/test/wpt/tests/fetch/metadata/generated/element-link-icon.sub.html
new file mode 100644
index 0000000..e9226c1
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-link-icon.sub.html
@@ -0,0 +1,279 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-link-icon.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for HTML "link" element with rel="icon"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ /**
+ * The `link` element supports a `load` event. That event would reliably
+ * indicate that the browser had received the request. Multiple major
+ * browsers do not implement the event, however, so in order to promote the
+ * visibility of this test, a less efficient polling-based detection
+ * mechanism is used.
+ *
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1638188
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=1083034
+ */
+ function induceRequest(t, url, attributes) {
+ const link = document.createElement('link');
+ link.setAttribute('rel', 'icon');
+ link.setAttribute('href', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ link.setAttribute(name, value);
+ }
+
+ document.head.appendChild(link);
+ t.add_cleanup(() => link.remove());
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent) no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], params),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-link-prefetch.https.optional.sub.html b/test/wpt/tests/fetch/metadata/generated/element-link-prefetch.https.optional.sub.html
new file mode 100644
index 0000000..bdd684a
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-link-prefetch.https.optional.sub.html
@@ -0,0 +1,559 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-link-prefetch.optional.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for HTML "link" element with rel="prefetch"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ /**
+ * The `link` element supports a `load` event. That event would reliably
+ * indicate that the browser had received the request. Multiple major
+ * browsers do not implement the event, however, so in order to promote the
+ * visibility of this test, a less efficient polling-based detection
+ * mechanism is used.
+ *
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1638188
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=1083034
+ */
+ function induceRequest(t, url, attributes) {
+ const link = document.createElement('link');
+ link.setAttribute('rel', 'prefetch');
+ link.setAttribute('href', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ link.setAttribute(name, value);
+ }
+
+ document.head.appendChild(link);
+ t.add_cleanup(() => link.remove());
+ }
+
+ setup(() => {
+ assert_implements_optional(document.createElement('link').relList.supports('prefetch'));
+ });
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"crossorigin": ""}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode attributes: crossorigin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"crossorigin": "anonymous"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode attributes: crossorigin=anonymous');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"crossorigin": "use-credentials"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode attributes: crossorigin=use-credentials');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['empty']);
+ });
+ }, 'sec-fetch-dest no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"as": "audio"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['audio']);
+ });
+ }, 'sec-fetch-dest attributes: as=audio');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"as": "document"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['document']);
+ });
+ }, 'sec-fetch-dest attributes: as=document');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"as": "embed"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['embed']);
+ });
+ }, 'sec-fetch-dest attributes: as=embed');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"as": "fetch"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['fetch']);
+ });
+ }, 'sec-fetch-dest attributes: as=fetch');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"as": "font"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['font']);
+ });
+ }, 'sec-fetch-dest attributes: as=font');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"as": "image"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['image']);
+ });
+ }, 'sec-fetch-dest attributes: as=image');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"as": "object"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['object']);
+ });
+ }, 'sec-fetch-dest attributes: as=object');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"as": "script"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['script']);
+ });
+ }, 'sec-fetch-dest attributes: as=script');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"as": "style"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['style']);
+ });
+ }, 'sec-fetch-dest attributes: as=style');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"as": "track"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['track']);
+ });
+ }, 'sec-fetch-dest attributes: as=track');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"as": "video"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['video']);
+ });
+ }, 'sec-fetch-dest attributes: as=video');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"as": "worker"}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['worker']);
+ });
+ }, 'sec-fetch-dest attributes: as=worker');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user no attributes');
+ </script>
+ </body>
+</html>
+
diff --git a/test/wpt/tests/fetch/metadata/generated/element-link-prefetch.optional.sub.html b/test/wpt/tests/fetch/metadata/generated/element-link-prefetch.optional.sub.html
new file mode 100644
index 0000000..c224488
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-link-prefetch.optional.sub.html
@@ -0,0 +1,275 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-link-prefetch.optional.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for HTML "link" element with rel="prefetch"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ /**
+ * The `link` element supports a `load` event. That event would reliably
+ * indicate that the browser had received the request. Multiple major
+ * browsers do not implement the event, however, so in order to promote the
+ * visibility of this test, a less efficient polling-based detection
+ * mechanism is used.
+ *
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1638188
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=1083034
+ */
+ function induceRequest(t, url, attributes) {
+ const link = document.createElement('link');
+ link.setAttribute('rel', 'prefetch');
+ link.setAttribute('href', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ link.setAttribute(name, value);
+ }
+
+ document.head.appendChild(link);
+ t.add_cleanup(() => link.remove());
+ }
+
+ setup(() => {
+ assert_implements_optional(document.createElement('link').relList.supports('prefetch'));
+ });
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent) no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']),
+ {}
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade no attributes');
+ </script>
+ </body>
+</html>
+
diff --git a/test/wpt/tests/fetch/metadata/generated/element-meta-refresh.https.optional.sub.html b/test/wpt/tests/fetch/metadata/generated/element-meta-refresh.https.optional.sub.html
new file mode 100644
index 0000000..3a1a8eb
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-meta-refresh.https.optional.sub.html
@@ -0,0 +1,276 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-meta-refresh.optional.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "meta" element with http-equiv="refresh"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test) {
+ const win = window.open();
+ test.add_cleanup(() => win.close());
+
+ win.document.open();
+ win.document.write(
+ `<meta http-equiv="Refresh" content="0; URL=${url}">`
+ );
+ win.document.close();
+
+ return new Promise((resolve) => {
+ addEventListener('message', (event) => {
+ if (event.source === win) {
+ resolve();
+ }
+ });
+ });
+ }
+
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>opener.postMessage(0, '*')</${''}script>`
+ };
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['navigate']);
+ });
+ }, 'sec-fetch-mode');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['document']);
+ });
+ }, 'sec-fetch-dest');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-meta-refresh.optional.sub.html b/test/wpt/tests/fetch/metadata/generated/element-meta-refresh.optional.sub.html
new file mode 100644
index 0000000..df3e92e
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-meta-refresh.optional.sub.html
@@ -0,0 +1,225 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-meta-refresh.optional.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "meta" element with http-equiv="refresh"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test) {
+ const win = window.open();
+ test.add_cleanup(() => win.close());
+
+ win.document.open();
+ win.document.write(
+ `<meta http-equiv="Refresh" content="0; URL=${url}">`
+ );
+ win.document.close();
+
+ return new Promise((resolve) => {
+ addEventListener('message', (event) => {
+ if (event.source === win) {
+ resolve();
+ }
+ });
+ });
+ }
+
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>opener.postMessage(0, '*')</${''}script>`
+ };
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-picture.https.sub.html b/test/wpt/tests/fetch/metadata/generated/element-picture.https.sub.html
new file mode 100644
index 0000000..ba6636a
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-picture.https.sub.html
@@ -0,0 +1,997 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-picture.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "picture" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, sourceEl, sourceAttr, attributes) {
+ const picture = document.createElement('picture');
+ const els = {
+ img: document.createElement('img'),
+ source: document.createElement('source')
+ };
+ picture.appendChild(els.source);
+ picture.appendChild(els.img);
+ document.body.appendChild(picture);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ els.img.setAttribute(name, value);
+ }
+
+ return new Promise((resolve) => {
+ els[sourceEl].setAttribute(sourceAttr, url);
+ els.img.onload = els.img.onerror = resolve;
+ });
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - img[src] - Same origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - img[srcset] - Same origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - source[srcset] - Same origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[src] - Cross-site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[srcset] - Cross-site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - source[srcset] - Cross-site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - img[src] - Same site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - img[srcset] - Same site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - source[srcset] - Same site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[src] - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[srcset] - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - source[srcset] - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - img[src] - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - img[srcset] - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - source[srcset] - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[src] - Cross-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[srcset] - Cross-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - source[srcset] - Cross-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[src] - Cross-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[srcset] - Cross-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - source[srcset] - Cross-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[src] - Cross-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[srcset] - Cross-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - source[srcset] - Cross-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - img[src] - Same-Origin -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - img[srcset] - Same-Origin -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - source[srcset] - Same-Origin -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - img[src] - Same-Origin -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - img[srcset] - Same-Origin -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - source[srcset] - Same-Origin -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[src] - Same-Origin -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[srcset] - Same-Origin -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - source[srcset] - Same-Origin -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - img[src] - Same-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - img[srcset] - Same-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - source[srcset] - Same-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - img[src] - Same-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - img[srcset] - Same-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - source[srcset] - Same-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[src] - Same-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[srcset] - Same-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - source[srcset] - Same-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - img[src] - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - img[srcset] - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - source[srcset] - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'img',
+ 'src',
+ {"crossorigin": ""}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - img[src] - attributes: crossorigin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'img',
+ 'srcset',
+ {"crossorigin": ""}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - img[srcset] - attributes: crossorigin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'source',
+ 'srcset',
+ {"crossorigin": ""}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - source[srcset] - attributes: crossorigin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'img',
+ 'src',
+ {"crossorigin": "anonymous"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - img[src] - attributes: crossorigin=anonymous');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'img',
+ 'srcset',
+ {"crossorigin": "anonymous"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - img[srcset] - attributes: crossorigin=anonymous');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'source',
+ 'srcset',
+ {"crossorigin": "anonymous"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - source[srcset] - attributes: crossorigin=anonymous');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'img',
+ 'src',
+ {"crossorigin": "use-credentials"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - img[src] - attributes: crossorigin=use-credentials');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'img',
+ 'srcset',
+ {"crossorigin": "use-credentials"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - img[srcset] - attributes: crossorigin=use-credentials');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'source',
+ 'srcset',
+ {"crossorigin": "use-credentials"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - source[srcset] - attributes: crossorigin=use-credentials');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['image']);
+ });
+ }, 'sec-fetch-dest - img[src] - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['image']);
+ });
+ }, 'sec-fetch-dest - img[srcset] - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['image']);
+ });
+ }, 'sec-fetch-dest - source[srcset] - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - img[src] - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - img[srcset] - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - source[srcset] - no attributes');
+ </script>
+ </body>
+</html>
+
diff --git a/test/wpt/tests/fetch/metadata/generated/element-picture.sub.html b/test/wpt/tests/fetch/metadata/generated/element-picture.sub.html
new file mode 100644
index 0000000..64f851c
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-picture.sub.html
@@ -0,0 +1,721 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-picture.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "picture" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, sourceEl, sourceAttr, attributes) {
+ const picture = document.createElement('picture');
+ const els = {
+ img: document.createElement('img'),
+ source: document.createElement('source')
+ };
+ picture.appendChild(els.source);
+ picture.appendChild(els.img);
+ document.body.appendChild(picture);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ els.img.setAttribute(name, value);
+ }
+
+ return new Promise((resolve) => {
+ els[sourceEl].setAttribute(sourceAttr, url);
+ els.img.onload = els.img.onerror = resolve;
+ });
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - img[src] - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - img[srcset] - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - source[srcset] - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - img[src] - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - img[srcset] - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - source[srcset] - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - img[src] - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - img[srcset] - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - source[srcset] - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - img[src] - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - img[srcset] - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - source[srcset] - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - img[src] - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - img[srcset] - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - source[srcset] - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - img[src] - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - img[srcset] - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - source[srcset] - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - img[src] - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - img[srcset] - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - source[srcset] - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - img[src] - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - img[srcset] - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - source[srcset] - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - img[src] - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - img[srcset] - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - source[srcset] - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - img[src] - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - img[srcset] - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - source[srcset] - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - img[src] - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - img[srcset] - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - source[srcset] - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - img[src] - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - img[srcset] - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - source[srcset] - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - img[src] - HTTPS downgrade (header not sent), no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - img[srcset] - HTTPS downgrade (header not sent), no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - source[srcset] - HTTPS downgrade (header not sent), no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[src] - HTTPS upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[srcset] - HTTPS upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - source[srcset] - HTTPS upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']),
+ 'img',
+ 'src',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[src] - HTTPS downgrade-upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']),
+ 'img',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - img[srcset] - HTTPS downgrade-upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']),
+ 'source',
+ 'srcset',
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - source[srcset] - HTTPS downgrade-upgrade, no attributes');
+ </script>
+ </body>
+</html>
+
diff --git a/test/wpt/tests/fetch/metadata/generated/element-script.https.sub.html b/test/wpt/tests/fetch/metadata/generated/element-script.https.sub.html
new file mode 100644
index 0000000..dcdcba2
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-script.https.sub.html
@@ -0,0 +1,593 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-script.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "script" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, attributes) {
+ const script = document.createElement('script');
+ script.setAttribute('src', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ script.setAttribute(name, value);
+ }
+
+ return new Promise((resolve, reject) => {
+ script.onload = resolve;
+ script.onerror = () => reject('Failed to load script');
+ document.body.appendChild(script);
+ })
+ .then(() => script.remove());
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite', 'httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite', 'httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite', 'httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite', 'httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite', 'httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite', 'httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite', 'httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite', 'httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"crossorigin": ""}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - attributes: crossorigin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"crossorigin": "anonymous"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - attributes: crossorigin=anonymous');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"crossorigin": "use-credentials"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - attributes: crossorigin=use-credentials');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['script']);
+ });
+ }, 'sec-fetch-dest - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - no attributes');
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-script.sub.html b/test/wpt/tests/fetch/metadata/generated/element-script.sub.html
new file mode 100644
index 0000000..a252669
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-script.sub.html
@@ -0,0 +1,488 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-script.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "script" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, attributes) {
+ const script = document.createElement('script');
+ script.setAttribute('src', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ script.setAttribute(name, value);
+ }
+
+ return new Promise((resolve, reject) => {
+ script.onload = resolve;
+ script.onerror = () => reject('Failed to load script');
+ document.body.appendChild(script);
+ })
+ .then(() => script.remove());
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent), no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent), attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade, attributes: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ {"type": "module"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade, attributes: type=module');
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-video-poster.https.sub.html b/test/wpt/tests/fetch/metadata/generated/element-video-poster.https.sub.html
new file mode 100644
index 0000000..5805b46
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-video-poster.https.sub.html
@@ -0,0 +1,243 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-video-poster.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "video" element "poster"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ function induceRequest(t, url) {
+ var video = document.createElement('video');
+ video.setAttribute('poster', url);
+ document.body.appendChild(video);
+
+ const poll = () => {
+ if (video.clientWidth === 123) {
+ return;
+ }
+
+ return new Promise((resolve) => t.step_timeout(resolve, 0))
+ .then(poll);
+ };
+ t.add_cleanup(() => video.remove());
+
+ return poll();
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, [], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, [], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['image']);
+ });
+ }, 'sec-fetch-dest');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, [], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-video-poster.sub.html b/test/wpt/tests/fetch/metadata/generated/element-video-poster.sub.html
new file mode 100644
index 0000000..e6cc5ee
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-video-poster.sub.html
@@ -0,0 +1,198 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-video-poster.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "video" element "poster"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ function induceRequest(t, url) {
+ var video = document.createElement('video');
+ video.setAttribute('poster', url);
+ document.body.appendChild(video);
+
+ const poll = () => {
+ if (video.clientWidth === 123) {
+ return;
+ }
+
+ return new Promise((resolve) => t.step_timeout(resolve, 0))
+ .then(poll);
+ };
+ t.add_cleanup(() => video.remove());
+
+ return poll();
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpSameSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpCrossSite'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-video.https.sub.html b/test/wpt/tests/fetch/metadata/generated/element-video.https.sub.html
new file mode 100644
index 0000000..971360d
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-video.https.sub.html
@@ -0,0 +1,325 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-video.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "video" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, attributes) {
+ const video = document.createElement('video');
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ video.setAttribute(name, value);
+ }
+
+ return new Promise((resolve) => {
+ video.setAttribute('src', url);
+ video.onload = video.onerror = resolve;
+ });
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {"crossorigin": ""}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - attributes: crossorigin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {"crossorigin": "anonymous"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - attributes: crossorigin=anonymous');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {"crossorigin": "use-credentials"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - attributes: crossorigin=use-credentials');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['video']);
+ });
+ }, 'sec-fetch-dest - no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/element-video.sub.html b/test/wpt/tests/fetch/metadata/generated/element-video.sub.html
new file mode 100644
index 0000000..9707413
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/element-video.sub.html
@@ -0,0 +1,229 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/element-video.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "video" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, attributes) {
+ const video = document.createElement('video');
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ video.setAttribute(name, value);
+ }
+
+ return new Promise((resolve) => {
+ video.setAttribute('src', url);
+ video.onload = video.onerror = resolve;
+ });
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent), no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade, no attributes');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade, no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/fetch-via-serviceworker.https.sub.html b/test/wpt/tests/fetch/metadata/generated/fetch-via-serviceworker.https.sub.html
new file mode 100644
index 0000000..22f9309
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/fetch-via-serviceworker.https.sub.html
@@ -0,0 +1,683 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/fetch-via-serviceworker.https.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request using the "fetch" API and passing through a Serive Worker</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const scripts = {
+ fallback: '/fetch/metadata/resources/fetch-via-serviceworker--fallback--sw.js',
+ respondWith: '/fetch/metadata/resources/fetch-via-serviceworker--respondWith--sw.js'
+ };
+
+ function induceRequest(t, url, init, script) {
+ const SCOPE = '/fetch/metadata/resources/fetch-via-serviceworker-frame.html';
+ const SCRIPT = scripts[script];
+
+ return service_worker_unregister_and_register(t, SCRIPT, SCOPE)
+ .then((registration) => {
+ t.add_cleanup(() => registration.unregister());
+
+ return wait_for_state(t, registration.installing, 'activated');
+ })
+ .then(() => with_iframe(SCOPE))
+ .then((frame) => {
+ t.add_cleanup(() => frame.remove());
+
+ return frame.contentWindow.fetch(url, init);
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin']),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin, init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin']),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin, init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite']),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site, init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite']),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site, init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite']),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site, init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite']),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site, init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin, init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin, init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site, init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site, init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site, init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site, init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin, init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin, init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site, init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site, init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site, init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site, init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin, init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin, init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site, init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site, init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site, init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site, init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - no init - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - no init - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"mode": "cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - init: mode=cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"mode": "cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - init: mode=cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"mode": "no-cors"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - init: mode=no-cors - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"mode": "no-cors"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - init: mode=no-cors - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"mode": "same-origin"},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['same-origin']);
+ });
+ }, 'sec-fetch-mode - init: mode=same-origin - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {"mode": "same-origin"},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['same-origin']);
+ });
+ }, 'sec-fetch-mode - init: mode=same-origin - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['empty']);
+ });
+ }, 'sec-fetch-dest - no init - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['empty']);
+ });
+ }, 'sec-fetch-dest - no init - fallback');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {},
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - no init - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, []),
+ {},
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - no init - fallback');
+
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/fetch.https.sub.html b/test/wpt/tests/fetch/metadata/generated/fetch.https.sub.html
new file mode 100644
index 0000000..dde1dae
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/fetch.https.sub.html
@@ -0,0 +1,302 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/fetch.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request using the "fetch" API</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, init) {
+ return fetch(url, init);
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin']),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin, init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite']),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site, init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite']),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site, init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin']),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect, init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin']),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect, init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin']),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin, init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite']),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site, init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite']),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site, init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin']),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin, init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite']),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site, init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite']),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site, init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin']),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin, init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite']),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site, init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite']),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site, init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {"mode": "cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode - init: mode=cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {"mode": "no-cors"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode - init: mode=no-cors');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {"mode": "same-origin"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['same-origin']);
+ });
+ }, 'sec-fetch-mode - init: mode=same-origin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['empty']);
+ });
+ }, 'sec-fetch-dest - no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, []),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - no init');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/fetch.sub.html b/test/wpt/tests/fetch/metadata/generated/fetch.sub.html
new file mode 100644
index 0000000..d28ea9b
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/fetch.sub.html
@@ -0,0 +1,220 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/fetch.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request using the "fetch" API</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, init) {
+ return fetch(url, init);
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination, no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination, no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination, no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination, no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination, no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination, no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination, no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination, no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent), no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade, no init');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin']),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade, no init');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/form-submission.https.sub.html b/test/wpt/tests/fetch/metadata/generated/form-submission.https.sub.html
new file mode 100644
index 0000000..988b07c
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/form-submission.https.sub.html
@@ -0,0 +1,522 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/form-submission.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for HTML form navigation</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(method, url, userActivated) {
+ const windowName = String(Math.random());
+ const form = document.createElement('form');
+ const submit = document.createElement('input');
+ submit.setAttribute('type', 'submit');
+ form.appendChild(submit);
+ const win = open('about:blank', windowName);
+ form.setAttribute('method', method);
+ form.setAttribute('action', url);
+ form.setAttribute('target', windowName);
+ document.body.appendChild(form);
+
+ // Query parameters must be expressed as form values so that they are sent
+ // with the submission of forms whose method is POST.
+ Array.from(new URL(url, location.origin).searchParams)
+ .forEach(([name, value]) => {
+ const input = document.createElement('input');
+ input.setAttribute('type', 'hidden');
+ input.setAttribute('name', name);
+ input.setAttribute('value', value);
+ form.appendChild(input);
+ });
+
+ return new Promise((resolve) => {
+ addEventListener('message', function(event) {
+ if (event.source === win) {
+ resolve();
+ }
+ });
+
+ if (userActivated) {
+ test_driver.click(submit);
+ } else {
+ submit.click();
+ }
+ })
+ .then(() => {
+ form.remove();
+ win.close();
+ });
+ }
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>opener.postMessage('done', '*')</${''}script>`
+ };
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['navigate']);
+ });
+ }, 'sec-fetch-mode - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['navigate']);
+ });
+ }, 'sec-fetch-mode - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['document']);
+ });
+ }, 'sec-fetch-dest - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['document']);
+ });
+ }, 'sec-fetch-dest - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+ const userActivated = true;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-user');
+ assert_array_equals(headers['sec-fetch-user'], ['?1']);
+ });
+ }, 'sec-fetch-user - GET with user activation');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+ const userActivated = true;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-user');
+ assert_array_equals(headers['sec-fetch-user'], ['?1']);
+ });
+ }, 'sec-fetch-user - POST with user activation');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/form-submission.sub.html b/test/wpt/tests/fetch/metadata/generated/form-submission.sub.html
new file mode 100644
index 0000000..f862062
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/form-submission.sub.html
@@ -0,0 +1,400 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/form-submission.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for HTML form navigation</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(method, url, userActivated) {
+ const windowName = String(Math.random());
+ const form = document.createElement('form');
+ const submit = document.createElement('input');
+ submit.setAttribute('type', 'submit');
+ form.appendChild(submit);
+ const win = open('about:blank', windowName);
+ form.setAttribute('method', method);
+ form.setAttribute('action', url);
+ form.setAttribute('target', windowName);
+ document.body.appendChild(form);
+
+ // Query parameters must be expressed as form values so that they are sent
+ // with the submission of forms whose method is POST.
+ Array.from(new URL(url, location.origin).searchParams)
+ .forEach(([name, value]) => {
+ const input = document.createElement('input');
+ input.setAttribute('type', 'hidden');
+ input.setAttribute('name', name);
+ input.setAttribute('value', value);
+ form.appendChild(input);
+ });
+
+ return new Promise((resolve) => {
+ addEventListener('message', function(event) {
+ if (event.source === win) {
+ resolve();
+ }
+ });
+
+ if (userActivated) {
+ test_driver.click(submit);
+ } else {
+ submit.click();
+ }
+ })
+ .then(() => {
+ form.remove();
+ win.close();
+ });
+ }
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>opener.postMessage('done', '*')</${''}script>`
+ };
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent) - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent) - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade - POST');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('GET', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - GET');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams);
+ const userActivated = false;
+ return induceRequest('POST', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - POST');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/header-link.https.sub.html b/test/wpt/tests/fetch/metadata/generated/header-link.https.sub.html
new file mode 100644
index 0000000..09f0113
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/header-link.https.sub.html
@@ -0,0 +1,529 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/header-link.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTTP "Link" header</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, rel, test) {
+ const iframe = document.createElement('iframe');
+
+ iframe.setAttribute(
+ 'src',
+ '/fetch/metadata/resources/header-link.py' +
+ `?location=${encodeURIComponent(url)}&rel=${rel}`
+ );
+
+ document.body.appendChild(iframe);
+ test.add_cleanup(() => iframe.remove());
+
+ return new Promise((resolve) => {
+ iframe.onload = iframe.onerror = resolve;
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site rel=icon - Same origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - Same origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=icon - Cross-site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - Cross-site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site rel=icon - Same site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - Same site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=icon - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site rel=icon - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=icon - Cross-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - Cross-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=icon - Cross-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - Cross-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=icon - Cross-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - Cross-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site rel=icon - Same-Origin -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - Same-Origin -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site rel=icon - Same-Origin -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - Same-Origin -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=icon - Same-Origin -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - Same-Origin -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site rel=icon - Same-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - Same-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site rel=icon - Same-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - Same-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=icon - Same-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - Same-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode rel=icon');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode rel=stylesheet');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['empty']);
+ });
+ }, 'sec-fetch-dest rel=icon');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user rel=icon');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user rel=stylesheet');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/header-link.https.sub.tentative.html b/test/wpt/tests/fetch/metadata/generated/header-link.https.sub.tentative.html
new file mode 100644
index 0000000..307c37f
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/header-link.https.sub.tentative.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/header-link.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTTP "Link" header</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, rel, test) {
+ const iframe = document.createElement('iframe');
+
+ iframe.setAttribute(
+ 'src',
+ '/fetch/metadata/resources/header-link.py' +
+ `?location=${encodeURIComponent(url)}&rel=${rel}`
+ );
+
+ document.body.appendChild(iframe);
+ test.add_cleanup(() => iframe.remove());
+
+ return new Promise((resolve) => {
+ iframe.onload = iframe.onerror = resolve;
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['style']);
+ });
+ }, 'sec-fetch-dest rel=stylesheet');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/header-link.sub.html b/test/wpt/tests/fetch/metadata/generated/header-link.sub.html
new file mode 100644
index 0000000..8b6cdae
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/header-link.sub.html
@@ -0,0 +1,460 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/header-link.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTTP "Link" header</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, rel, test) {
+ const iframe = document.createElement('iframe');
+
+ iframe.setAttribute(
+ 'src',
+ '/fetch/metadata/resources/header-link.py' +
+ `?location=${encodeURIComponent(url)}&rel=${rel}`
+ );
+
+ document.body.appendChild(iframe);
+ test.add_cleanup(() => iframe.remove());
+
+ return new Promise((resolve) => {
+ iframe.onload = iframe.onerror = resolve;
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site rel=icon - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site rel=stylesheet - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site rel=icon - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site rel=stylesheet - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site rel=icon - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site rel=stylesheet - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode rel=icon - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode rel=stylesheet - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode rel=icon - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode rel=stylesheet - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode rel=icon - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode rel=stylesheet - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest rel=icon - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest rel=stylesheet - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest rel=icon - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest rel=stylesheet - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest rel=icon - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest rel=stylesheet - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user rel=icon - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user rel=stylesheet - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user rel=icon - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user rel=stylesheet - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user rel=icon - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user rel=stylesheet - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site rel=icon - HTTPS downgrade (header not sent)');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site rel=stylesheet - HTTPS downgrade (header not sent)');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=icon - HTTPS upgrade');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - HTTPS upgrade');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ 'icon',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=icon - HTTPS downgrade-upgrade');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], {mime: 'text/html'}),
+ 'stylesheet',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site rel=stylesheet - HTTPS downgrade-upgrade');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/header-refresh.https.optional.sub.html b/test/wpt/tests/fetch/metadata/generated/header-refresh.https.optional.sub.html
new file mode 100644
index 0000000..e63ee42
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/header-refresh.https.optional.sub.html
@@ -0,0 +1,273 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/header-refresh.optional.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for HTTP "Refresh" header</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test) {
+ const win = window.open();
+ test.add_cleanup(() => win.close());
+
+ win.location = `/common/refresh.py?location=${encodeURIComponent(url)}`
+
+ return new Promise((resolve) => {
+ addEventListener('message', (event) => {
+ if (event.source === win) {
+ resolve();
+ }
+ });
+ });
+ }
+
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>opener.postMessage(0, '*')</${''}script>`
+ };
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['navigate']);
+ });
+ }, 'sec-fetch-mode');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['document']);
+ });
+ }, 'sec-fetch-dest');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/header-refresh.optional.sub.html b/test/wpt/tests/fetch/metadata/generated/header-refresh.optional.sub.html
new file mode 100644
index 0000000..4674ada
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/header-refresh.optional.sub.html
@@ -0,0 +1,222 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/header-refresh.optional.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for HTTP "Refresh" header</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test) {
+ const win = window.open();
+ test.add_cleanup(() => win.close());
+
+ win.location = `/common/refresh.py?location=${encodeURIComponent(url)}`
+
+ return new Promise((resolve) => {
+ addEventListener('message', (event) => {
+ if (event.source === win) {
+ resolve();
+ }
+ });
+ });
+ }
+
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>opener.postMessage(0, '*')</${''}script>`
+ };
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpSameSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpCrossSite'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/script-module-import-dynamic.https.sub.html b/test/wpt/tests/fetch/metadata/generated/script-module-import-dynamic.https.sub.html
new file mode 100644
index 0000000..72d60fc
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/script-module-import-dynamic.https.sub.html
@@ -0,0 +1,254 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/script-module-import-dynamic.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for dynamic ECMAScript module import</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <script type="module">
+ 'use strict';
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite', 'httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite', 'httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite', 'httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite', 'httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['script']);
+ });
+ }, 'sec-fetch-dest');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user');
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/script-module-import-dynamic.sub.html b/test/wpt/tests/fetch/metadata/generated/script-module-import-dynamic.sub.html
new file mode 100644
index 0000000..088720c
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/script-module-import-dynamic.sub.html
@@ -0,0 +1,214 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/script-module-import-dynamic.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for dynamic ECMAScript module import</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <script type="module">
+ 'use strict';
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade');
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/script-module-import-static.https.sub.html b/test/wpt/tests/fetch/metadata/generated/script-module-import-static.https.sub.html
new file mode 100644
index 0000000..cea3464
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/script-module-import-static.https.sub.html
@@ -0,0 +1,288 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/script-module-import-static.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for static ECMAScript module import</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url) {
+ const script = document.createElement('script');
+ script.setAttribute('type', 'module');
+ script.setAttribute(
+ 'src',
+ '/fetch/metadata/resources/es-module.sub.js?moduleId=' + encodeURIComponent(url)
+ );
+
+ return new Promise((resolve, reject) => {
+ script.onload = resolve;
+ script.onerror = () => reject('Failed to load script');
+ document.body.appendChild(script);
+ })
+ .then(() => script.remove());
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsOrigin'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsCrossSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsSameSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsCrossSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsCrossSite', 'httpsSameSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsCrossSite', 'httpsCrossSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsOrigin', 'httpsSameSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsOrigin', 'httpsCrossSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsSameSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsSameSite', 'httpsSameSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsSameSite', 'httpsCrossSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['script']);
+ });
+ }, 'sec-fetch-dest');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user');
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/script-module-import-static.sub.html b/test/wpt/tests/fetch/metadata/generated/script-module-import-static.sub.html
new file mode 100644
index 0000000..0f94f71
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/script-module-import-static.sub.html
@@ -0,0 +1,246 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/script-module-import-static.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for static ECMAScript module import</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url) {
+ const script = document.createElement('script');
+ script.setAttribute('type', 'module');
+ script.setAttribute(
+ 'src',
+ '/fetch/metadata/resources/es-module.sub.js?moduleId=' + encodeURIComponent(url)
+ );
+
+ return new Promise((resolve, reject) => {
+ script.onload = resolve;
+ script.onerror = () => reject('Failed to load script');
+ document.body.appendChild(script);
+ })
+ .then(() => script.remove());
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsOrigin', 'httpOrigin'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade');
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/serviceworker.https.sub.html b/test/wpt/tests/fetch/metadata/generated/serviceworker.https.sub.html
new file mode 100644
index 0000000..12e3736
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/serviceworker.https.sub.html
@@ -0,0 +1,170 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/serviceworker.https.sub.html
+-->
+<!DOCTYPE html>
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for Service Workers</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(t, url, options, event, clear) {
+ // Register a service worker and check the request header.
+ return navigator.serviceWorker.register(url, options)
+ .then((registration) => {
+ t.add_cleanup(() => registration.unregister());
+ if (event === 'register') {
+ return;
+ }
+ return clear().then(() => registration.update());
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(t, url, {}, 'register')
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin, no options - registration');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(t, url, {}, 'update', () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin, no options - updating');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(t, url, {"type": "classic"}, 'register')
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['same-origin']);
+ });
+ }, 'sec-fetch-mode - options: type=classic - registration');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(t, url, {"type": "classic"}, 'update', () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['same-origin']);
+ });
+ }, 'sec-fetch-mode - options: type=classic - updating');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(t, url, {}, 'register')
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['same-origin']);
+ });
+ }, 'sec-fetch-mode - no options - registration');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(t, url, {}, 'update', () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['same-origin']);
+ });
+ }, 'sec-fetch-mode - no options - updating');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(t, url, {}, 'register')
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['serviceworker']);
+ });
+ }, 'sec-fetch-dest - no options - registration');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(t, url, {}, 'update', () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['serviceworker']);
+ });
+ }, 'sec-fetch-dest - no options - updating');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(t, url, {}, 'register')
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - no options - registration');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(t, url, {}, 'update', () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - no options - updating');
+ </script>
+</body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/svg-image.https.sub.html b/test/wpt/tests/fetch/metadata/generated/svg-image.https.sub.html
new file mode 100644
index 0000000..b059eb3
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/svg-image.https.sub.html
@@ -0,0 +1,367 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/svg-image.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for SVG "image" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ function induceRequest(t, url, attributes) {
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ svg.setAttributeNS(
+ "http://www.w3.org/2000/xmlns/",
+ "xmlns:xlink",
+ "http://www.w3.org/1999/xlink"
+ );
+ const image = document.createElementNS("http://www.w3.org/2000/svg", "image");
+ image.setAttribute("href", url);
+ svg.appendChild(image);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ image.setAttribute(name, value);
+ }
+
+ document.body.appendChild(svg);
+ t.add_cleanup(() => svg.remove());
+
+ return new Promise((resolve, reject) => {
+ image.onload = resolve;
+ image.onerror = reject;
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {"crossorigin": ""}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode attributes: crossorigin');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {"crossorigin": "anonymous"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode attributes: crossorigin=anonymous');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {"crossorigin": "use-credentials"}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['cors']);
+ });
+ }, 'sec-fetch-mode attributes: crossorigin=use-credentials');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['empty']);
+ });
+ }, 'sec-fetch-dest no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/svg-image.sub.html b/test/wpt/tests/fetch/metadata/generated/svg-image.sub.html
new file mode 100644
index 0000000..a28bbb1
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/svg-image.sub.html
@@ -0,0 +1,265 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/svg-image.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for SVG "image" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ function induceRequest(t, url, attributes) {
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ svg.setAttributeNS(
+ "http://www.w3.org/2000/xmlns/",
+ "xmlns:xlink",
+ "http://www.w3.org/1999/xlink"
+ );
+ const image = document.createElementNS("http://www.w3.org/2000/svg", "image");
+ image.setAttribute("href", url);
+ svg.appendChild(image);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ image.setAttribute(name, value);
+ }
+
+ document.body.appendChild(svg);
+ t.add_cleanup(() => svg.remove());
+
+ return new Promise((resolve, reject) => {
+ image.onload = resolve;
+ image.onerror = reject;
+ });
+ }
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpSameSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpCrossSite'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent) no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade no attributes');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], params),
+ {}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade no attributes');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/window-history.https.sub.html b/test/wpt/tests/fetch/metadata/generated/window-history.https.sub.html
new file mode 100644
index 0000000..c2b3079
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/window-history.https.sub.html
@@ -0,0 +1,237 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/window-history.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for navigation via the HTML History API</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const whenDone = (win) => {
+ return new Promise((resolve) => {
+ addEventListener('message', function handle(event) {
+ if (event.source === win) {
+ resolve();
+ removeEventListener('message', handle);
+ }
+ });
+ })
+ };
+
+ /**
+ * Prime the UA's session history such that the location of the request is
+ * immediately behind the current entry. Because the location may not be
+ * same-origin with the current browsing context, this must be done via a
+ * true navigation and not, e.g. the `history.pushState` API. The initial
+ * navigation will alter the WPT server's internal state; in order to avoid
+ * false positives, clear that state prior to initiating the second
+ * navigation via `history.back`.
+ */
+ function induceBackRequest(url, test, clear) {
+ const win = window.open(url);
+
+ test.add_cleanup(() => win.close());
+
+ return whenDone(win)
+ .then(clear)
+ .then(() => win.history.back())
+ .then(() => whenDone(win));
+ }
+
+ /**
+ * Prime the UA's session history such that the location of the request is
+ * immediately ahead of the current entry. Because the location may not be
+ * same-origin with the current browsing context, this must be done via a
+ * true navigation and not, e.g. the `history.pushState` API. The initial
+ * navigation will alter the WPT server's internal state; in order to avoid
+ * false positives, clear that state prior to initiating the second
+ * navigation via `history.forward`.
+ */
+ function induceForwardRequest(url, test, clear) {
+ const win = window.open(messageOpenerUrl);
+
+ test.add_cleanup(() => win.close());
+
+ return whenDone(win)
+ .then(() => win.location = url)
+ .then(() => whenDone(win))
+ .then(clear)
+ .then(() => win.history.go(-2))
+ .then(() => whenDone(win))
+ .then(() => win.history.forward())
+ .then(() => whenDone(win));
+ }
+
+ const messageOpenerUrl = new URL(
+ '/fetch/metadata/resources/message-opener.html', location
+ );
+ // For these tests to function, replacement must *not* be enabled during
+ // navigation. Assignment must therefore take place after the document has
+ // completely loaded [1]. This event is not directly observable, but it is
+ // scheduled as a task immediately following the global object's `load`
+ // event [2]. By queuing a task during the dispatch of the `load` event,
+ // navigation can be consistently triggered without replacement.
+ //
+ // [1] https://html.spec.whatwg.org/multipage/history.html#location-object-setter-navigate
+ // [2] https://html.spec.whatwg.org/multipage/parsing.html#the-end
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>
+ window.addEventListener('load', () => {
+ set`+`Timeout(() => location.assign('${messageOpenerUrl}'));
+ });
+ <`+`/script>`
+ };
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['navigate']);
+ });
+ }, 'sec-fetch-mode - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['navigate']);
+ });
+ }, 'sec-fetch-mode - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['document']);
+ });
+ }, 'sec-fetch-dest - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['document']);
+ });
+ }, 'sec-fetch-dest - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - history.forward');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/window-history.sub.html b/test/wpt/tests/fetch/metadata/generated/window-history.sub.html
new file mode 100644
index 0000000..333d90c
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/window-history.sub.html
@@ -0,0 +1,360 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/window-history.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for navigation via the HTML History API</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const whenDone = (win) => {
+ return new Promise((resolve) => {
+ addEventListener('message', function handle(event) {
+ if (event.source === win) {
+ resolve();
+ removeEventListener('message', handle);
+ }
+ });
+ })
+ };
+
+ /**
+ * Prime the UA's session history such that the location of the request is
+ * immediately behind the current entry. Because the location may not be
+ * same-origin with the current browsing context, this must be done via a
+ * true navigation and not, e.g. the `history.pushState` API. The initial
+ * navigation will alter the WPT server's internal state; in order to avoid
+ * false positives, clear that state prior to initiating the second
+ * navigation via `history.back`.
+ */
+ function induceBackRequest(url, test, clear) {
+ const win = window.open(url);
+
+ test.add_cleanup(() => win.close());
+
+ return whenDone(win)
+ .then(clear)
+ .then(() => win.history.back())
+ .then(() => whenDone(win));
+ }
+
+ /**
+ * Prime the UA's session history such that the location of the request is
+ * immediately ahead of the current entry. Because the location may not be
+ * same-origin with the current browsing context, this must be done via a
+ * true navigation and not, e.g. the `history.pushState` API. The initial
+ * navigation will alter the WPT server's internal state; in order to avoid
+ * false positives, clear that state prior to initiating the second
+ * navigation via `history.forward`.
+ */
+ function induceForwardRequest(url, test, clear) {
+ const win = window.open(messageOpenerUrl);
+
+ test.add_cleanup(() => win.close());
+
+ return whenDone(win)
+ .then(() => win.location = url)
+ .then(() => whenDone(win))
+ .then(clear)
+ .then(() => win.history.go(-2))
+ .then(() => whenDone(win))
+ .then(() => win.history.forward())
+ .then(() => whenDone(win));
+ }
+
+ const messageOpenerUrl = new URL(
+ '/fetch/metadata/resources/message-opener.html', location
+ );
+ // For these tests to function, replacement must *not* be enabled during
+ // navigation. Assignment must therefore take place after the document has
+ // completely loaded [1]. This event is not directly observable, but it is
+ // scheduled as a task immediately following the global object's `load`
+ // event [2]. By queuing a task during the dispatch of the `load` event,
+ // navigation can be consistently triggered without replacement.
+ //
+ // [1] https://html.spec.whatwg.org/multipage/history.html#location-object-setter-navigate
+ // [2] https://html.spec.whatwg.org/multipage/parsing.html#the-end
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>
+ window.addEventListener('load', () => {
+ set`+`Timeout(() => location.assign('${messageOpenerUrl}'));
+ });
+ <`+`/script>`
+ };
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination - history.forward');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination - history.back');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination - history.forward');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/window-location.https.sub.html b/test/wpt/tests/fetch/metadata/generated/window-location.https.sub.html
new file mode 100644
index 0000000..4a0d2fd
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/window-location.https.sub.html
@@ -0,0 +1,1184 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/window-location.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for navigation via the HTML Location API</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, navigate, userActivated) {
+ const win = window.open();
+
+ return new Promise((resolve) => {
+ addEventListener('message', function(event) {
+ if (event.source === win) {
+ resolve();
+ }
+ });
+
+ if (userActivated) {
+ test_driver.bless('enable user activation', () => {
+ navigate(win, url);
+ });
+ } else {
+ navigate(win, url);
+ }
+ })
+ .then(() => win.close());
+ }
+
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>opener.postMessage('done', '*')</${''}script>`
+ };
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsCrossSite', 'httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsSameSite', 'httpsCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['navigate']);
+ });
+ }, 'sec-fetch-mode - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['navigate']);
+ });
+ }, 'sec-fetch-mode - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['navigate']);
+ });
+ }, 'sec-fetch-mode - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['navigate']);
+ });
+ }, 'sec-fetch-mode - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['document']);
+ });
+ }, 'sec-fetch-dest - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['document']);
+ });
+ }, 'sec-fetch-dest - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['document']);
+ });
+ }, 'sec-fetch-dest - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['document']);
+ });
+ }, 'sec-fetch-dest - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, true)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-user');
+ assert_array_equals(headers['sec-fetch-user'], ['?1']);
+ });
+ }, 'sec-fetch-user - location with user activation');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, true)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-user');
+ assert_array_equals(headers['sec-fetch-user'], ['?1']);
+ });
+ }, 'sec-fetch-user - location.href with user activation');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, true)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-user');
+ assert_array_equals(headers['sec-fetch-user'], ['?1']);
+ });
+ }, 'sec-fetch-user - location.assign with user activation');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, true)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-user');
+ assert_array_equals(headers['sec-fetch-user'], ['?1']);
+ });
+ }, 'sec-fetch-user - location.replace with user activation');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/window-location.sub.html b/test/wpt/tests/fetch/metadata/generated/window-location.sub.html
new file mode 100644
index 0000000..bb3e680
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/window-location.sub.html
@@ -0,0 +1,894 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/window-location.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for navigation via the HTML Location API</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, navigate, userActivated) {
+ const win = window.open();
+
+ return new Promise((resolve) => {
+ addEventListener('message', function(event) {
+ if (event.source === win) {
+ resolve();
+ }
+ });
+
+ if (userActivated) {
+ test_driver.bless('enable user activation', () => {
+ navigate(win, url);
+ });
+ } else {
+ navigate(win, url);
+ }
+ })
+ .then(() => win.close());
+ }
+
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>opener.postMessage('done', '*')</${''}script>`
+ };
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpSameSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpCrossSite'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent) - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent) - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent) - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent) - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade - location.replace');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - location');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - location.href');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - location.assign');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, false)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade - location.replace');
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/worker-dedicated-constructor.https.sub.html b/test/wpt/tests/fetch/metadata/generated/worker-dedicated-constructor.https.sub.html
new file mode 100644
index 0000000..86f1760
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/worker-dedicated-constructor.https.sub.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/worker-dedicated-constructor.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for dedicated worker via the "Worker" constructor</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <script type="module">
+ 'use strict';
+ function induceRequest(url, options) {
+ return new Promise((resolve, reject) => {
+ const worker = new Worker(url, options);
+ worker.onmessage = resolve;
+ worker.onerror = reject;
+ });
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ [],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['same-origin']);
+ });
+ }, 'sec-fetch-mode - no options');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ [],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url, {"type": "module"})
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['same-origin']);
+ });
+ }, 'sec-fetch-mode - options: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ [],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['worker']);
+ });
+ }, 'sec-fetch-dest - no options');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ [],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url, {"type": "module"})
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['worker']);
+ });
+ }, 'sec-fetch-dest - options: type=module');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ [],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - no options');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ [],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url, {"type": "module"})
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - options: type=module');
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/worker-dedicated-constructor.sub.html b/test/wpt/tests/fetch/metadata/generated/worker-dedicated-constructor.sub.html
new file mode 100644
index 0000000..69ac768
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/worker-dedicated-constructor.sub.html
@@ -0,0 +1,204 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/worker-dedicated-constructor.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for dedicated worker via the "Worker" constructor</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <script type="module">
+ 'use strict';
+ function induceRequest(url, options) {
+ return new Promise((resolve, reject) => {
+ const worker = new Worker(url, options);
+ worker.onmessage = resolve;
+ worker.onerror = reject;
+ });
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ ['httpOrigin'],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination, no options');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ ['httpSameSite'],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination, no options');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ ['httpCrossSite'],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination, no options');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ ['httpOrigin'],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no options');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ ['httpSameSite'],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination, no options');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ ['httpCrossSite'],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no options');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ ['httpOrigin'],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination, no options');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ ['httpSameSite'],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination, no options');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ ['httpCrossSite'],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination, no options');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ ['httpOrigin'],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination, no options');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ ['httpSameSite'],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination, no options');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ ['httpCrossSite'],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination, no options');
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/worker-dedicated-importscripts.https.sub.html b/test/wpt/tests/fetch/metadata/generated/worker-dedicated-importscripts.https.sub.html
new file mode 100644
index 0000000..0cd9f35
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/worker-dedicated-importscripts.https.sub.html
@@ -0,0 +1,268 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/worker-dedicated-importscripts.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for dedicated worker via the "importScripts" API</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <script type="module">
+ 'use strict';
+ function induceRequest(url, options) {
+ const src = `
+ importScripts('${url}');
+ postMessage('done');
+ `;
+ const workerUrl = URL.createObjectURL(
+ new Blob([src], { type: 'application/javascript' })
+ );
+ return new Promise((resolve, reject) => {
+ const worker = new Worker(workerUrl, options);
+ worker.onmessage = resolve;
+ worker.onerror = reject;
+ });
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same origin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsCrossSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsSameSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same Origin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite', 'httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Same-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsCrossSite', 'httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Cross-Site -> Cross-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-origin']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same Origin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Same-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Origin -> Cross-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same Origin');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite', 'httpsSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['same-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Same-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsSameSite', 'httpsCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - Same-Site -> Cross-Site');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-mode');
+ assert_array_equals(headers['sec-fetch-mode'], ['no-cors']);
+ });
+ }, 'sec-fetch-mode');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-dest');
+ assert_array_equals(headers['sec-fetch-dest'], ['script']);
+ });
+ }, 'sec-fetch-dest');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user');
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/generated/worker-dedicated-importscripts.sub.html b/test/wpt/tests/fetch/metadata/generated/worker-dedicated-importscripts.sub.html
new file mode 100644
index 0000000..0555bba
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/generated/worker-dedicated-importscripts.sub.html
@@ -0,0 +1,228 @@
+<!DOCTYPE html>
+<!--
+This test was procedurally generated. Please do not modify it directly.
+Sources:
+- fetch/metadata/tools/fetch-metadata.conf.yml
+- fetch/metadata/tools/templates/worker-dedicated-importscripts.sub.html
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for dedicated worker via the "importScripts" API</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <script type="module">
+ 'use strict';
+ function induceRequest(url, options) {
+ const src = `
+ importScripts('${url}');
+ postMessage('done');
+ `;
+ const workerUrl = URL.createObjectURL(
+ new Blob([src], { type: 'application/javascript' })
+ );
+ return new Promise((resolve, reject) => {
+ const worker = new Worker(workerUrl, options);
+ worker.onmessage = resolve;
+ worker.onerror = reject;
+ });
+ }
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-origin destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy same-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - Not sent to non-trustworthy cross-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-origin destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy same-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-mode');
+ });
+ }, 'sec-fetch-mode - Not sent to non-trustworthy cross-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-origin destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy same-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-dest');
+ });
+ }, 'sec-fetch-dest - Not sent to non-trustworthy cross-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-origin destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpSameSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy same-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpCrossSite'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-user');
+ });
+ }, 'sec-fetch-user - Not sent to non-trustworthy cross-site destination');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_not_own_property(headers, 'sec-fetch-site');
+ });
+ }, 'sec-fetch-site - HTTPS downgrade (header not sent)');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS upgrade');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, ['httpsOrigin', 'httpOrigin', 'httpsOrigin'], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ assert_own_property(headers, 'sec-fetch-site');
+ assert_array_equals(headers['sec-fetch-site'], ['cross-site']);
+ });
+ }, 'sec-fetch-site - HTTPS downgrade-upgrade');
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/navigation.https.sub.html b/test/wpt/tests/fetch/metadata/navigation.https.sub.html
new file mode 100644
index 0000000..32c9cf7
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/navigation.https.sub.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script>
+ test(t => {
+ let expected = {
+ "mode": "navigate",
+ "site": "none",
+ "dest": "document"
+ };
+
+ let actual = {
+ "mode": "{{headers[sec-fetch-mode]}}",
+ "site": "{{headers[sec-fetch-site]}}",
+ // Skipping `Sec-Fetch-User`, as the test harness isn't consistent here.
+ "dest": "{{headers[sec-fetch-dest]}}"
+ };
+
+ assert_header_equals(actual, expected);
+ }, "This page's top-level navigation.");
+</script>
diff --git a/test/wpt/tests/fetch/metadata/object.https.sub.html b/test/wpt/tests/fetch/metadata/object.https.sub.html
new file mode 100644
index 0000000..fae5b37
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/object.https.sub.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<link rel="author" href="mtrzos@google.com" title="Maciek Trzos">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<body>
+<script>
+ let nonce = token();
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = "object-same-origin" + nonce;
+
+ let e = document.createElement('object');
+ e.data = "https://{{host}}:{{ports[https][0]}}/fetch/metadata/resources/record-header.py?file=" + key;
+ e.onload = e => {
+ let expected = {"site":"same-origin", "user":"", "mode":"navigate", "dest": "object"};
+ validate_expectations(key, expected, "Same-Origin object")
+ .then(_ => resolve())
+ .catch(e => reject(e));
+ };
+
+ document.body.appendChild(e);
+ })
+ }, "Same-Origin object");
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = "object-same-site" + nonce;
+
+ let e = document.createElement('object');
+ e.data = "https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/metadata/resources/record-header.py?file=" + key;
+ e.onload = e => {
+ let expected = {"site":"same-site", "user":"", "mode":"navigate", "dest": "object"};
+ validate_expectations(key, expected, "Same-Site object")
+ .then(_ => resolve())
+ .catch(e => reject(e));
+ };
+
+ document.body.appendChild(e);
+ })
+ }, "Same-Site object");
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = "object-cross-site" + nonce;
+
+ let e = document.createElement('object');
+ e.data = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/metadata/resources/record-header.py?file=" + key;
+ e.onload = e => {
+ let expected = {"site":"cross-site", "user":"", "mode":"navigate", "dest": "object"};
+ validate_expectations(key, expected, "Cross-Site object")
+ .then(_ => resolve())
+ .catch(e => reject(e));
+ };
+
+ document.body.appendChild(e);
+ })
+ }, "Cross-Site object");
+</script>
diff --git a/test/wpt/tests/fetch/metadata/paint-worklet.https.html b/test/wpt/tests/fetch/metadata/paint-worklet.https.html
new file mode 100644
index 0000000..49fc776
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/paint-worklet.https.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<script>
+
+ promise_test(async t => {
+ const nonce = token();
+ const key = "worklet-destination" + nonce;
+
+ await CSS.paintWorklet.addModule("/fetch/metadata/resources/record-header.py?file=" + key);
+ const expected = {"site": "same-origin", "user": "", "mode": "cors", "dest": "paintworklet"};
+ await validate_expectations(key, expected);
+ }, "The fetch metadata for paint worklet");
+
+</script>
+<body></body>
diff --git a/test/wpt/tests/fetch/metadata/portal.https.sub.html b/test/wpt/tests/fetch/metadata/portal.https.sub.html
new file mode 100644
index 0000000..55b555a
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/portal.https.sub.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<script src=/portals/resources/stash-utils.sub.js></script>
+<body>
+<script>
+ const USER = true;
+ const FORCED = false;
+
+ function create_test(host, expectations) {
+ async_test(t => {
+ assert_implements("HTMLPortalElement" in window, "Portals are not supported.");
+
+ let p = document.createElement('portal');
+ const key = token();
+ StashUtils.takeValue(key).then(t.step_func_done(value => {
+ assert_header_equals(value, expectations, `{{host}} -> ${host} portal`);
+ }));
+
+ let url = `https://${host}/fetch/metadata/resources/post-to-owner.py?key=${key}`;
+ p.src = url;
+ document.body.appendChild(p);
+ }, `{{host}} -> ${host} portal`);
+ }
+
+ create_test("{{host}}:{{ports[https][0]}}", {
+ "site": "same-origin",
+ "user": "",
+ "mode": "navigate",
+ "dest": "iframe"
+ });
+
+ create_test("{{hosts[][www]}}:{{ports[https][0]}}", {
+ "site": "same-site",
+ "user": "",
+ "mode": "navigate",
+ "dest": "iframe"
+ });
+
+ create_test("{{hosts[alt][www]}}:{{ports[https][0]}}", {
+ "site": "cross-site",
+ "user": "",
+ "mode": "navigate",
+ "dest": "iframe"
+ });
+</script>
diff --git a/test/wpt/tests/fetch/metadata/preload.https.sub.html b/test/wpt/tests/fetch/metadata/preload.https.sub.html
new file mode 100644
index 0000000..29042a8
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/preload.https.sub.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta name="timeout" content="long">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<body></body>
+<script>
+ test(t => {
+ assert_true(document.createElement('link').relList.supports('preload'));
+ }, "Browser supports preload.");
+
+ function create_test(host, as, expected) {
+ async_test(t => {
+ let nonce = token();
+ let key = as + nonce;
+
+ let e = document.createElement('link');
+ e.rel = "preload";
+ e.href = `https://${host}/fetch/metadata/resources/record-header.py?file=${key}`;
+ e.setAttribute("crossorigin", "crossorigin");
+ if (as !== undefined) {
+ e.setAttribute("as", as);
+ }
+ e.onload = e.onerror = t.step_func(e => {
+ fetch("/fetch/metadata/resources/record-header.py?retrieve=true&file=" + key)
+ .then(t.step_func(response => response.text()))
+ .then(t.step_func_done(text => assert_header_equals(text, expected, `preload ${as} ${host}`)))
+ .catch(t.unreached_func());
+ });
+
+ document.head.appendChild(e);
+ }, `<link rel='preload' as='${as}' href='https://${host}/...'>`);
+ }
+
+ let as_tests = [
+ [ "fetch", "empty" ],
+ [ "font", "font" ],
+ [ "image", "image" ],
+ [ "script", "script" ],
+ [ "style", "style" ],
+ [ "track", "track" ],
+ ];
+
+ as_tests.forEach(item => {
+ create_test("{{host}}:{{ports[https][0]}}", item[0], {"site":"same-origin", "user":"", "mode": "cors", "dest": item[1]});
+ create_test("{{hosts[][www]}}:{{ports[https][0]}}", item[0], {"site":"same-site", "user":"", "mode": "cors", "dest": item[1]});
+ create_test("{{hosts[alt][www]}}:{{ports[https][0]}}", item[0], {"site":"cross-site", "user":"", "mode": "cors", "dest": item[1]});
+ });
+</script>
diff --git a/test/wpt/tests/fetch/metadata/redirect/multiple-redirect-https-downgrade-upgrade.sub.html b/test/wpt/tests/fetch/metadata/redirect/multiple-redirect-https-downgrade-upgrade.sub.html
new file mode 100644
index 0000000..0f8f320
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/redirect/multiple-redirect-https-downgrade-upgrade.sub.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/fetch/metadata/resources/redirectTestHelper.sub.js></script>
+<script src=/common/security-features/resources/common.sub.js></script>
+<script src=/common/utils.js></script>
+<body>
+<script>
+ let nonce = token();
+ let expected = {"site": "cross-site", "user": "", "mode": "cors", "dest": "font"};
+
+ // Validate various scenarios handle a request that redirects from https => http => https
+ // correctly and avoids disclosure of any Sec- headers.
+ RunCommonRedirectTests("Https downgrade-upgrade", MultipleRedirectTo, expected);
+</script>
+</body>
diff --git a/test/wpt/tests/fetch/metadata/redirect/redirect-http-upgrade.sub.html b/test/wpt/tests/fetch/metadata/redirect/redirect-http-upgrade.sub.html
new file mode 100644
index 0000000..fa765b6
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/redirect/redirect-http-upgrade.sub.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/fetch/metadata/resources/redirectTestHelper.sub.js></script>
+<script src=/common/security-features/resources/common.sub.js></script>
+<script src=/common/utils.js></script>
+<body>
+<script>
+ let nonce = token();
+ let expected = { "site": "cross-site", "user": "", "mode": "cors", "dest": "font" };
+
+ // Validate various scenarios handle a request that redirects from http => https correctly and add the proper Sec- headers.
+ RunCommonRedirectTests("Http upgrade", upgradeRedirectTo, expected);
+</script>
+</body>
diff --git a/test/wpt/tests/fetch/metadata/redirect/redirect-https-downgrade.sub.html b/test/wpt/tests/fetch/metadata/redirect/redirect-https-downgrade.sub.html
new file mode 100644
index 0000000..4e5a48e
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/redirect/redirect-https-downgrade.sub.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/fetch/metadata/resources/redirectTestHelper.sub.js></script>
+<script src=/common/security-features/resources/common.sub.js></script>
+<script src=/common/utils.js></script>
+<body>
+ <script>
+ let nonce = token();
+ let expected = { "site": "", "user": "", "mode": "", "dest": "" };
+
+ // Validate various scenarios handle a request that redirects from https => http correctly and avoids disclosure of any Sec- headers.
+ RunCommonRedirectTests("Https downgrade", downgradeRedirectTo, expected);
+</script>
+</body>
diff --git a/test/wpt/tests/fetch/metadata/report.https.sub.html b/test/wpt/tests/fetch/metadata/report.https.sub.html
new file mode 100644
index 0000000..b65f7c0
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/report.https.sub.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<link rel="author" href="mtrzos@google.com" title="Maciek Trzos">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script>
+ setup({ explicit_done: true });
+ function generate_test(expected, name) {
+ async_test(t => {
+ t.step_timeout(_ => {
+ return validate_expectations("report-" + name, expected, name + " report")
+ .then(_ => t.done());
+ }, 1000);
+ }, name + " report");
+ }
+
+ let counter = 0;
+ document.addEventListener("securitypolicyviolation", (e) => {
+ counter++;
+ if (counter == 3) {
+ generate_test({"site":"same-origin", "user":"", "mode": "no-cors", "dest": "report"}, "same-origin");
+ generate_test({"site":"same-site", "user":"", "mode": "no-cors", "dest": "report"}, "same-site");
+ generate_test({"site":"cross-site", "user":"", "mode": "no-cors", "dest": "report"}, "cross-site");
+
+ done();
+ }
+ });
+</script>
+
+<!-- The hostname here is unimportant, so long as it doesn't match 'self'. -->
+<link id="style" href="https://{{hosts[alt][élève]}}:{{ports[https][0]}}/css/support/a-green.css" rel="stylesheet">
+
+<body></body>
diff --git a/test/wpt/tests/fetch/metadata/report.https.sub.html.sub.headers b/test/wpt/tests/fetch/metadata/report.https.sub.html.sub.headers
new file mode 100644
index 0000000..1ec5df7
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/report.https.sub.html.sub.headers
@@ -0,0 +1,3 @@
+Content-Security-Policy: style-src 'self' 'unsafe-inline'; report-uri /fetch/metadata/resources/record-header.py?file=report-same-origin
+Content-Security-Policy: style-src 'self' 'unsafe-inline'; report-uri https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/metadata/resources/record-header.py?file=report-same-site
+Content-Security-Policy: style-src 'self' 'unsafe-inline'; report-uri https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/metadata/resources/record-header.py?file=report-cross-site
diff --git a/test/wpt/tests/fetch/metadata/resources/appcache-iframe.sub.html b/test/wpt/tests/fetch/metadata/resources/appcache-iframe.sub.html
new file mode 100644
index 0000000..cea9a4f
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/appcache-iframe.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en" manifest="{{GET[manifest]}}">
+<script>
+if (!window.applicationCache) {
+ parent.postMessage('application cache not supported');
+} else {
+ applicationCache.onnoupdate =
+ applicationCache.ondownloading =
+ applicationCache.onobsolete =
+ applicationCache.onerror = function() {
+ parent.postMessage('okay');
+ };
+}
+</script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/resources/dedicatedWorker.js b/test/wpt/tests/fetch/metadata/resources/dedicatedWorker.js
new file mode 100644
index 0000000..18626d3
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/dedicatedWorker.js
@@ -0,0 +1 @@
+self.postMessage("Loaded");
diff --git a/test/wpt/tests/fetch/metadata/resources/echo-as-json.py b/test/wpt/tests/fetch/metadata/resources/echo-as-json.py
new file mode 100644
index 0000000..44f68e8
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/echo-as-json.py
@@ -0,0 +1,29 @@
+import json
+
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+ headers = [(b"Content-Type", b"application/json"),
+ (b"Access-Control-Allow-Credentials", b"true")]
+
+ if b"origin" in request.headers:
+ headers.append((b"Access-Control-Allow-Origin", request.headers[b"origin"]))
+
+ body = u""
+
+ # If we're in a preflight, verify that `Sec-Fetch-Mode` is `cors`.
+ if request.method == u'OPTIONS':
+ if request.headers.get(b"sec-fetch-mode") != b"cors":
+ return (403, b"Failed"), [], body
+
+ headers.append((b"Access-Control-Allow-Methods", b"*"))
+ headers.append((b"Access-Control-Allow-Headers", b"*"))
+ else:
+ body = json.dumps({
+ u"dest": isomorphic_decode(request.headers.get(b"sec-fetch-dest", b"")),
+ u"mode": isomorphic_decode(request.headers.get(b"sec-fetch-mode", b"")),
+ u"site": isomorphic_decode(request.headers.get(b"sec-fetch-site", b"")),
+ u"user": isomorphic_decode(request.headers.get(b"sec-fetch-user", b"")),
+ })
+
+ return headers, body
diff --git a/test/wpt/tests/fetch/metadata/resources/echo-as-script.py b/test/wpt/tests/fetch/metadata/resources/echo-as-script.py
new file mode 100644
index 0000000..1e7bc91
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/echo-as-script.py
@@ -0,0 +1,14 @@
+import json
+
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+ headers = [(b"Content-Type", b"text/javascript")]
+ body = u"var header = %s;" % json.dumps({
+ u"dest": isomorphic_decode(request.headers.get(b"sec-fetch-dest", b"")),
+ u"mode": isomorphic_decode(request.headers.get(b"sec-fetch-mode", b"")),
+ u"site": isomorphic_decode(request.headers.get(b"sec-fetch-site", b"")),
+ u"user": isomorphic_decode(request.headers.get(b"sec-fetch-user", b"")),
+ })
+
+ return headers, body
diff --git a/test/wpt/tests/fetch/metadata/resources/es-module.sub.js b/test/wpt/tests/fetch/metadata/resources/es-module.sub.js
new file mode 100644
index 0000000..f9668a3
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/es-module.sub.js
@@ -0,0 +1 @@
+import '{{GET[moduleId]}}';
diff --git a/test/wpt/tests/fetch/metadata/resources/fetch-via-serviceworker--fallback--sw.js b/test/wpt/tests/fetch/metadata/resources/fetch-via-serviceworker--fallback--sw.js
new file mode 100644
index 0000000..09858b2
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/fetch-via-serviceworker--fallback--sw.js
@@ -0,0 +1,3 @@
+self.addEventListener('fetch', function(event) {
+ // Empty event handler - will fallback to the network.
+});
diff --git a/test/wpt/tests/fetch/metadata/resources/fetch-via-serviceworker--respondWith--sw.js b/test/wpt/tests/fetch/metadata/resources/fetch-via-serviceworker--respondWith--sw.js
new file mode 100644
index 0000000..8bf8d8f
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/fetch-via-serviceworker--respondWith--sw.js
@@ -0,0 +1,3 @@
+self.addEventListener('fetch', function(event) {
+ event.respondWith(fetch(event.request));
+});
diff --git a/test/wpt/tests/fetch/metadata/resources/fetch-via-serviceworker-frame.html b/test/wpt/tests/fetch/metadata/resources/fetch-via-serviceworker-frame.html
new file mode 100644
index 0000000..9879802
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/fetch-via-serviceworker-frame.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Page Title</title>
diff --git a/test/wpt/tests/fetch/metadata/resources/header-link.py b/test/wpt/tests/fetch/metadata/resources/header-link.py
new file mode 100644
index 0000000..de89116
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/header-link.py
@@ -0,0 +1,15 @@
+def main(request, response):
+ """
+ Respond with a blank HTML document and a `Link` header which describes
+ a link relation specified by the requests `location` and `rel` query string
+ parameters
+ """
+ headers = [
+ (b'Content-Type', b'text/html'),
+ (
+ b'Link',
+ b'<' + request.GET.first(b'location') + b'>; rel=' + request.GET.first(b'rel')
+ )
+ ]
+ return (200, headers, b'')
+
diff --git a/test/wpt/tests/fetch/metadata/resources/helper.js b/test/wpt/tests/fetch/metadata/resources/helper.js
new file mode 100644
index 0000000..725f9a7
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/helper.js
@@ -0,0 +1,42 @@
+function validate_expectations(key, expected, tag) {
+ return fetch("/fetch/metadata/resources/record-header.py?retrieve=true&file=" + key)
+ .then(response => response.text())
+ .then(text => {
+ assert_not_equals(text, "No header has been recorded");
+ let value = JSON.parse(text);
+ test(t => assert_equals(value.dest, expected.dest), `${tag}: sec-fetch-dest`);
+ test(t => assert_equals(value.mode, expected.mode), `${tag}: sec-fetch-mode`);
+ test(t => assert_equals(value.site, expected.site), `${tag}: sec-fetch-site`);
+ test(t => assert_equals(value.user, expected.user), `${tag}: sec-fetch-user`);
+ });
+}
+
+function validate_expectations_custom_url(url, header, expected, tag) {
+ return fetch(url, header)
+ .then(response => response.text())
+ .then(text => {
+ assert_not_equals(text, "No header has been recorded");
+ let value = JSON.parse(text);
+ test(t => assert_equals(value.dest, expected.dest), `${tag}: sec-fetch-dest`);
+ test(t => assert_equals(value.mode, expected.mode), `${tag}: sec-fetch-mode`);
+ test(t => assert_equals(value.site, expected.site), `${tag}: sec-fetch-site`);
+ test(t => assert_equals(value.user, expected.user), `${tag}: sec-fetch-user`);
+ });
+}
+
+/**
+ * @param {object} value
+ * @param {object} expected
+ * @param {string} tag
+ **/
+function assert_header_equals(value, expected, tag) {
+ if (typeof(value) === "string"){
+ assert_not_equals(value, "No header has been recorded");
+ value = JSON.parse(value);
+ }
+
+ test(t => assert_equals(value.dest, expected.dest), `${tag}: sec-fetch-dest`);
+ test(t => assert_equals(value.mode, expected.mode), `${tag}: sec-fetch-mode`);
+ test(t => assert_equals(value.site, expected.site), `${tag}: sec-fetch-site`);
+ test(t => assert_equals(value.user, expected.user), `${tag}: sec-fetch-user`);
+}
diff --git a/test/wpt/tests/fetch/metadata/resources/helper.sub.js b/test/wpt/tests/fetch/metadata/resources/helper.sub.js
new file mode 100644
index 0000000..fd179fe
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/helper.sub.js
@@ -0,0 +1,67 @@
+'use strict';
+
+/**
+ * Construct a URL which, when followed, will trigger redirection through zero
+ * or more specified origins and ultimately resolve in the Python handler
+ * `record-headers.py`.
+ *
+ * @param {string} key - the WPT server "stash" name where the request's
+ * headers should be stored
+ * @param {string[]} [origins] - zero or more origin names through which the
+ * request should pass; see the function
+ * implementation for a completel list of names
+ * and corresponding origins; If specified, the
+ * final origin will be used to access the
+ * `record-headers.py` hander.
+ * @param {object} [params] - a collection of key-value pairs to include as
+ * URL "search" parameters in the final request to
+ * `record-headers.py`
+ *
+ * @returns {string} an absolute URL
+ */
+function makeRequestURL(key, origins, params) {
+ const byName = {
+ httpOrigin: 'http://{{host}}:{{ports[http][0]}}',
+ httpSameSite: 'http://{{hosts[][www]}}:{{ports[http][0]}}',
+ httpCrossSite: 'http://{{hosts[alt][]}}:{{ports[http][0]}}',
+ httpsOrigin: 'https://{{host}}:{{ports[https][0]}}',
+ httpsSameSite: 'https://{{hosts[][www]}}:{{ports[https][0]}}',
+ httpsCrossSite: 'https://{{hosts[alt][]}}:{{ports[https][0]}}'
+ };
+ const redirectPath = '/fetch/api/resources/redirect.py?location=';
+ const path = '/fetch/metadata/resources/record-headers.py?key=' + key;
+
+ let requestUrl = path;
+ if (params) {
+ requestUrl += '&' + new URLSearchParams(params).toString();
+ }
+
+ if (origins && origins.length) {
+ requestUrl = byName[origins.pop()] + requestUrl;
+
+ while (origins.length) {
+ requestUrl = byName[origins.pop()] + redirectPath +
+ encodeURIComponent(requestUrl);
+ }
+ } else {
+ requestUrl = byName.httpsOrigin + requestUrl;
+ }
+
+ return requestUrl;
+}
+
+function retrieve(key, options) {
+ return fetch('/fetch/metadata/resources/record-headers.py?retrieve&key=' + key)
+ .then((response) => {
+ if (response.status === 204 && options && options.poll) {
+ return new Promise((resolve) => setTimeout(resolve, 300))
+ .then(() => retrieve(key, options));
+ }
+
+ if (response.status !== 200) {
+ throw new Error('Failed to query for recorded headers.');
+ }
+
+ return response.text().then((text) => JSON.parse(text));
+ });
+}
diff --git a/test/wpt/tests/fetch/metadata/resources/message-opener.html b/test/wpt/tests/fetch/metadata/resources/message-opener.html
new file mode 100644
index 0000000..eb2af7b
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/message-opener.html
@@ -0,0 +1,17 @@
+<script>
+/**
+ * Send a message to the opening browsing context when the document is
+ * "completely loaded" (a condition which occurs immediately after the `load`
+ * and `pageshow` events are fired).
+ * https://html.spec.whatwg.org/multipage/parsing.html#the-end
+ */
+'use strict';
+
+// The `pageshow` event is used instead of the `load` event because this
+// document may itself be accessed via history traversal. In such cases, the
+// browser may choose to reuse a cached document and therefore fire no
+// additional `load` events.
+addEventListener('pageshow', () => {
+ setTimeout(() => opener.postMessage(null, '*'), 0);
+});
+</script>
diff --git a/test/wpt/tests/fetch/metadata/resources/post-to-owner.py b/test/wpt/tests/fetch/metadata/resources/post-to-owner.py
new file mode 100644
index 0000000..256dd6e
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/post-to-owner.py
@@ -0,0 +1,36 @@
+import json
+
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+ headers = [
+ (b"Content-Type", b"text/html"),
+ (b"Cache-Control", b"no-cache, no-store, must-revalidate")
+ ]
+ key = request.GET.first(b"key", None)
+
+ # We serialize the key into JSON, so have to decode it first.
+ if key is not None:
+ key = key.decode('utf-8')
+
+ body = u"""
+ <!DOCTYPE html>
+ <script src="/portals/resources/stash-utils.sub.js"></script>
+ <script>
+ var data = %s;
+ if (window.opener)
+ window.opener.postMessage(data, "*");
+ if (window.top != window)
+ window.top.postMessage(data, "*");
+
+ const key = %s;
+ if (key)
+ StashUtils.putValue(key, data);
+ </script>
+ """ % (json.dumps({
+ u"dest": isomorphic_decode(request.headers.get(b"sec-fetch-dest", b"")),
+ u"mode": isomorphic_decode(request.headers.get(b"sec-fetch-mode", b"")),
+ u"site": isomorphic_decode(request.headers.get(b"sec-fetch-site", b"")),
+ u"user": isomorphic_decode(request.headers.get(b"sec-fetch-user", b"")),
+ }), json.dumps(key))
+ return headers, body
diff --git a/test/wpt/tests/fetch/metadata/resources/record-header.py b/test/wpt/tests/fetch/metadata/resources/record-header.py
new file mode 100644
index 0000000..29ff2ed
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/record-header.py
@@ -0,0 +1,145 @@
+import os
+import hashlib
+import json
+
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+ ## Get the query parameter (key) from URL ##
+ ## Tests will record POST requests (CSP Report) and GET (rest) ##
+ if request.GET:
+ key = request.GET[b'file']
+ elif request.POST:
+ key = request.POST[b'file']
+
+ ## Convert the key from String to UUID valid String ##
+ testId = hashlib.md5(key).hexdigest()
+
+ ## Handle the header retrieval request ##
+ if b'retrieve' in request.GET:
+ response.writer.write_status(200)
+ response.writer.write_header(b"Connection", b"close")
+ response.writer.end_headers()
+ try:
+ header_value = request.server.stash.take(testId)
+ response.writer.write(header_value)
+ except (KeyError, ValueError) as e:
+ response.writer.write(u"No header has been recorded")
+ pass
+
+ response.close_connection = True
+
+ ## Record incoming fetch metadata header value
+ else:
+ try:
+ ## Return a serialized JSON object with one member per header. If the ##
+ ## header isn't present, the member will contain an empty string. ##
+ header = json.dumps({
+ u"dest": isomorphic_decode(request.headers.get(b"sec-fetch-dest", b"")),
+ u"mode": isomorphic_decode(request.headers.get(b"sec-fetch-mode", b"")),
+ u"site": isomorphic_decode(request.headers.get(b"sec-fetch-site", b"")),
+ u"user": isomorphic_decode(request.headers.get(b"sec-fetch-user", b"")),
+ })
+ request.server.stash.put(testId, header)
+ except KeyError:
+ ## The header is already recorded or it doesn't exist
+ pass
+
+ ## Prevent the browser from caching returned responses and allow CORS ##
+ response.headers.set(b"Access-Control-Allow-Origin", b"*")
+ response.headers.set(b"Cache-Control", b"no-cache, no-store, must-revalidate")
+ response.headers.set(b"Pragma", b"no-cache")
+ response.headers.set(b"Expires", b"0")
+
+ ## Add a valid ServiceWorker Content-Type ##
+ if key.startswith(b"serviceworker"):
+ response.headers.set(b"Content-Type", b"application/javascript")
+
+ ## Add a valid image Content-Type ##
+ if key.startswith(b"image"):
+ response.headers.set(b"Content-Type", b"image/png")
+ file = open(os.path.join(request.doc_root, u"media", u"1x1-green.png"), u"rb")
+ image = file.read()
+ file.close()
+ return image
+
+ ## Return a valid .vtt content for the <track> tag ##
+ if key.startswith(b"track"):
+ return b"WEBVTT"
+
+ ## Return a valid SharedWorker ##
+ if key.startswith(b"sharedworker"):
+ response.headers.set(b"Content-Type", b"application/javascript")
+ file = open(os.path.join(request.doc_root, u"fetch", u"metadata",
+ u"resources", u"sharedWorker.js"), u"rb")
+ shared_worker = file.read()
+ file.close()
+ return shared_worker
+
+ ## Return a valid font content and Content-Type ##
+ if key.startswith(b"font"):
+ response.headers.set(b"Content-Type", b"application/x-font-ttf")
+ file = open(os.path.join(request.doc_root, u"fonts", u"Ahem.ttf"), u"rb")
+ font = file.read()
+ file.close()
+ return font
+
+ ## Return a valid audio content and Content-Type ##
+ if key.startswith(b"audio"):
+ response.headers.set(b"Content-Type", b"audio/mpeg")
+ file = open(os.path.join(request.doc_root, u"media", u"sound_5.mp3"), u"rb")
+ audio = file.read()
+ file.close()
+ return audio
+
+ ## Return a valid video content and Content-Type ##
+ if key.startswith(b"video"):
+ response.headers.set(b"Content-Type", b"video/mp4")
+ file = open(os.path.join(request.doc_root, u"media", u"A4.mp4"), u"rb")
+ video = file.read()
+ file.close()
+ return video
+
+ ## Return valid style content and Content-Type ##
+ if key.startswith(b"style"):
+ response.headers.set(b"Content-Type", b"text/css")
+ return b"div { }"
+
+ ## Return a valid embed/object content and Content-Type ##
+ if key.startswith(b"embed") or key.startswith(b"object"):
+ response.headers.set(b"Content-Type", b"text/html")
+ return b"<html>EMBED!</html>"
+
+ ## Return a valid image content and Content-Type for redirect requests ##
+ if key.startswith(b"redirect"):
+ response.headers.set(b"Content-Type", b"image/jpeg")
+ file = open(os.path.join(request.doc_root, u"media", u"1x1-green.png"), u"rb")
+ image = file.read()
+ file.close()
+ return image
+
+ ## Return a valid dedicated worker
+ if key.startswith(b"worker"):
+ response.headers.set(b"Content-Type", b"application/javascript")
+ return b"self.postMessage('loaded');"
+
+ ## Return a valid worklet
+ if key.startswith(b"worklet"):
+ response.headers.set(b"Content-Type", b"application/javascript")
+ return b""
+
+ ## Return a valid XSLT
+ if key.startswith(b"xslt"):
+ response.headers.set(b"Content-Type", b"text/xsl")
+ return b"""<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()"/>
+ </xsl:copy>
+ </xsl:template>
+</xsl:stylesheet>"""
+
+ if key.startswith(b"script"):
+ response.headers.set(b"Content-Type", b"application/javascript")
+ return b"void 0;"
diff --git a/test/wpt/tests/fetch/metadata/resources/record-headers.py b/test/wpt/tests/fetch/metadata/resources/record-headers.py
new file mode 100644
index 0000000..0362fe2
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/record-headers.py
@@ -0,0 +1,73 @@
+import os
+import uuid
+import hashlib
+import time
+import json
+
+
+def bytes_to_strings(d):
+ # Recursively convert bytes to strings in `d`.
+ if not isinstance(d, dict):
+ if isinstance(d, (tuple,list,set)):
+ v = [bytes_to_strings(x) for x in d]
+ return v
+ else:
+ if isinstance(d, bytes):
+ d = d.decode()
+ return d
+
+ result = {}
+ for k,v in d.items():
+ if isinstance(k, bytes):
+ k = k.decode()
+ if isinstance(v, dict):
+ v = bytes_to_strings(v)
+ elif isinstance(v, (tuple,list,set)):
+ v = [bytes_to_strings(x) for x in v]
+ elif isinstance(v, bytes):
+ v = v.decode()
+ result[k] = v
+ return result
+
+
+def main(request, response):
+ # This condition avoids false positives from CORS preflight checks, where the
+ # request under test may be followed immediately by a request to the same URL
+ # using a different HTTP method.
+ if b'requireOPTIONS' in request.GET and request.method != b'OPTIONS':
+ return
+
+ if b'key' in request.GET:
+ key = request.GET[b'key']
+ elif b'key' in request.POST:
+ key = request.POST[b'key']
+
+ ## Convert the key from String to UUID valid String ##
+ testId = hashlib.md5(key).hexdigest()
+
+ ## Handle the header retrieval request ##
+ if b'retrieve' in request.GET:
+ recorded_headers = request.server.stash.take(testId)
+
+ if recorded_headers is None:
+ return (204, [], b'')
+
+ return (200, [], recorded_headers)
+
+ ## Record incoming fetch metadata header value
+ else:
+ try:
+ request.server.stash.put(testId, json.dumps(bytes_to_strings(request.headers)))
+ except KeyError:
+ ## The header is already recorded or it doesn't exist
+ pass
+
+ ## Prevent the browser from caching returned responses and allow CORS ##
+ response.headers.set(b"Access-Control-Allow-Origin", b"*")
+ response.headers.set(b"Cache-Control", b"no-cache, no-store, must-revalidate")
+ response.headers.set(b"Pragma", b"no-cache")
+ response.headers.set(b"Expires", b"0")
+ if b"mime" in request.GET:
+ response.headers.set(b"Content-Type", request.GET.first(b"mime"))
+
+ return request.GET.first(b"body", request.POST.first(b"body", b""))
diff --git a/test/wpt/tests/fetch/metadata/resources/redirectTestHelper.sub.js b/test/wpt/tests/fetch/metadata/resources/redirectTestHelper.sub.js
new file mode 100644
index 0000000..1bfbbae
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/redirectTestHelper.sub.js
@@ -0,0 +1,167 @@
+function createVideoElement() {
+ let el = document.createElement('video');
+ el.src = '/media/movie_5.mp4';
+ el.setAttribute('controls', '');
+ el.setAttribute('crossorigin', '');
+ return el;
+}
+
+function createTrack() {
+ let el = document.createElement('track');
+ el.setAttribute('default', '');
+ el.setAttribute('kind', 'captions');
+ el.setAttribute('srclang', 'en');
+ return el;
+}
+
+let secureRedirectURL = 'https://{{host}}:{{ports[https][0]}}/fetch/api/resources/redirect.py?location=';
+let insecureRedirectURL = 'http://{{host}}:{{ports[http][0]}}/fetch/api/resources/redirect.py?location=';
+let secureTestURL = 'https://{{host}}:{{ports[https][0]}}/fetch/metadata/';
+let insecureTestURL = 'http://{{host}}:{{ports[http][0]}}/fetch/metadata/';
+
+// Helper to craft an URL that will go from HTTPS => HTTP => HTTPS to
+// simulate us downgrading then upgrading again during the same redirect chain.
+function MultipleRedirectTo(partialPath) {
+ let finalURL = insecureRedirectURL + encodeURIComponent(secureTestURL + partialPath);
+ return secureRedirectURL + encodeURIComponent(finalURL);
+}
+
+// Helper to craft an URL that will go from HTTP => HTTPS to simulate upgrading a
+// given request.
+function upgradeRedirectTo(partialPath) {
+ return insecureRedirectURL + encodeURIComponent(secureTestURL + partialPath);
+}
+
+// Helper to craft an URL that will go from HTTPS => HTTP to simulate downgrading a
+// given request.
+function downgradeRedirectTo(partialPath) {
+ return secureRedirectURL + encodeURIComponent(insecureTestURL + partialPath);
+}
+
+// Helper to run common redirect test cases that don't require special setup on
+// the test page itself.
+function RunCommonRedirectTests(testNamePrefix, urlHelperMethod, expectedResults) {
+ async_test(t => {
+ let testWindow = window.open(urlHelperMethod('resources/post-to-owner.py?top-level-navigation' + nonce));
+ t.add_cleanup(_ => testWindow.close());
+ window.addEventListener('message', t.step_func(e => {
+ if (e.source != testWindow) {
+ return;
+ }
+
+ let expectation = { ...expectedResults };
+ if (expectation['mode'] != '')
+ expectation['mode'] = 'navigate';
+ if (expectation['dest'] == 'font')
+ expectation['dest'] = 'document';
+ assert_header_equals(e.data, expectation, testNamePrefix + ' top level navigation');
+ t.done();
+ }));
+ }, testNamePrefix + ' top level navigation');
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = 'embed-https-redirect' + nonce;
+ let e = document.createElement('embed');
+ e.src = urlHelperMethod('resources/record-header.py?file=' + key);
+ e.onload = e => {
+ let expectation = { ...expectedResults };
+ if (expectation['mode'] != '')
+ expectation['mode'] = 'navigate';
+ if (expectation['dest'] == 'font')
+ expectation['dest'] = 'embed';
+ fetch('/fetch/metadata/resources/record-header.py?retrieve=true&file=' + key)
+ .then(response => response.text())
+ .then(t.step_func(text => assert_header_equals(text, expectation, testNamePrefix + ' embed')))
+ .then(resolve)
+ .catch(e => reject(e));
+ };
+ document.body.appendChild(e);
+ });
+ }, testNamePrefix + ' embed');
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = 'object-https-redirect' + nonce;
+ let e = document.createElement('object');
+ e.data = urlHelperMethod('resources/record-header.py?file=' + key);
+ e.onload = e => {
+ let expectation = { ...expectedResults };
+ if (expectation['mode'] != '')
+ expectation['mode'] = 'navigate';
+ if (expectation['dest'] == 'font')
+ expectation['dest'] = 'object';
+ fetch('/fetch/metadata/resources/record-header.py?retrieve=true&file=' + key)
+ .then(response => response.text())
+ .then(t.step_func(text => assert_header_equals(text, expectation, testNamePrefix + ' object')))
+ .then(resolve)
+ .catch(e => reject(e));
+ };
+ document.body.appendChild(e);
+ });
+ }, testNamePrefix + ' object');
+
+ if (document.createElement('link').relList.supports('preload')) {
+ async_test(t => {
+ let key = 'preload' + nonce;
+ let e = document.createElement('link');
+ e.rel = 'preload';
+ e.href = urlHelperMethod('resources/record-header.py?file=' + key);
+ e.setAttribute('as', 'track');
+ e.onload = e.onerror = t.step_func_done(e => {
+ let expectation = { ...expectedResults };
+ if (expectation['mode'] != '')
+ expectation['mode'] = 'cors';
+ fetch('/fetch/metadata/resources/record-header.py?retrieve=true&file=' + key)
+ .then(t.step_func(response => response.text()))
+ .then(t.step_func_done(text => assert_header_equals(text, expectation, testNamePrefix + ' preload')))
+ .catch(t.unreached_func());
+ });
+ document.head.appendChild(e);
+ }, testNamePrefix + ' preload');
+ }
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = 'style-https-redirect' + nonce;
+ let e = document.createElement('link');
+ e.rel = 'stylesheet';
+ e.href = urlHelperMethod('resources/record-header.py?file=' + key);
+ e.onload = e => {
+ let expectation = { ...expectedResults };
+ if (expectation['mode'] != '')
+ expectation['mode'] = 'no-cors';
+ if (expectation['dest'] == 'font')
+ expectation['dest'] = 'style';
+ fetch('/fetch/metadata/resources/record-header.py?retrieve=true&file=' + key)
+ .then(response => response.text())
+ .then(t.step_func(text => assert_header_equals(text, expectation, testNamePrefix + ' stylesheet')))
+ .then(resolve)
+ .catch(e => reject(e));
+ };
+ document.body.appendChild(e);
+ });
+ }, testNamePrefix + ' stylesheet');
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = 'track-https-redirect' + nonce;
+ let video = createVideoElement();
+ let el = createTrack();
+ el.src = urlHelperMethod('resources/record-header.py?file=' + key);
+ el.onload = t.step_func(_ => {
+ let expectation = { ...expectedResults };
+ if (expectation['mode'] != '')
+ expectation['mode'] = 'cors';
+ if (expectation['dest'] == 'font')
+ expectation['dest'] = 'track';
+ fetch('/fetch/metadata/resources/record-header.py?retrieve=true&file=' + key)
+ .then(response => response.text())
+ .then(t.step_func(text => assert_header_equals(text, expectation, testNamePrefix + ' track')))
+ .then(resolve);
+ });
+ video.appendChild(el);
+ document.body.appendChild(video);
+ });
+ }, testNamePrefix + ' track');
+}
diff --git a/test/wpt/tests/fetch/metadata/resources/serviceworker-accessors-frame.html b/test/wpt/tests/fetch/metadata/resources/serviceworker-accessors-frame.html
new file mode 100644
index 0000000..9879802
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/serviceworker-accessors-frame.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Page Title</title>
diff --git a/test/wpt/tests/fetch/metadata/resources/serviceworker-accessors.sw.js b/test/wpt/tests/fetch/metadata/resources/serviceworker-accessors.sw.js
new file mode 100644
index 0000000..36c55a7
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/serviceworker-accessors.sw.js
@@ -0,0 +1,14 @@
+addEventListener("fetch", event => {
+ event.waitUntil(async function () {
+ if (!event.clientId) return;
+ const client = await clients.get(event.clientId);
+ if (!client) return;
+
+ client.postMessage({
+ "dest": event.request.headers.get("sec-fetch-dest"),
+ "mode": event.request.headers.get("sec-fetch-mode"),
+ "site": event.request.headers.get("sec-fetch-site"),
+ "user": event.request.headers.get("sec-fetch-user")
+ });
+ }());
+});
diff --git a/test/wpt/tests/fetch/metadata/resources/sharedWorker.js b/test/wpt/tests/fetch/metadata/resources/sharedWorker.js
new file mode 100644
index 0000000..5eb89cb
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/sharedWorker.js
@@ -0,0 +1,9 @@
+onconnect = function(e) {
+ var port = e.ports[0];
+
+ port.addEventListener('message', function(e) {
+ port.postMessage("Ready");
+ });
+
+ port.start();
+}
diff --git a/test/wpt/tests/fetch/metadata/resources/unload-with-beacon.html b/test/wpt/tests/fetch/metadata/resources/unload-with-beacon.html
new file mode 100644
index 0000000..b00c9a5
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/unload-with-beacon.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ // When told, register an unload handler that will trigger a beacon to the
+ // URL given by the sender of the message.
+ window.addEventListener('message', e => {
+ var url = e.data;
+ window.addEventListener('unload', () => {
+ navigator.sendBeacon(url, 'blah');
+ });
+ window.parent.postMessage('navigate-away', '*');
+ });
+</script>
diff --git a/test/wpt/tests/fetch/metadata/resources/xslt-test.sub.xml b/test/wpt/tests/fetch/metadata/resources/xslt-test.sub.xml
new file mode 100644
index 0000000..acb478a
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/resources/xslt-test.sub.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="https://{{host}}:{{ports[https][0]}}/fetch/metadata/resources/record-header.py?file=xslt-same-origin{{GET[token]}}" type="text/xsl" ?>
+<!-- Only testing same-origin XSLT because same-site and cross-site XSLT is blocked. -->
+
+<!-- postMessage parent back when the resources are loaded -->
+<script xmlns="http://www.w3.org/1999/xhtml"><![CDATA[
+ setTimeout(function(){
+ if (window.opener)
+ window.opener.postMessage("", "*");
+ if (window.top != window)
+ window.top.postMessage("", "*");}, 100);
+]]></script>
diff --git a/test/wpt/tests/fetch/metadata/serviceworker-accessors.https.sub.html b/test/wpt/tests/fetch/metadata/serviceworker-accessors.https.sub.html
new file mode 100644
index 0000000..03a8321
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/serviceworker-accessors.https.sub.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!--
+ This test verifies that Fetch Metadata headers are not exposed to Service
+ Workers via the request's `headers` accessor.
+-->
+<meta charset="utf-8"/>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script src=/common/utils.js></script>
+<script>
+ const SCOPE = 'resources/serviceworker-accessors-frame.html';
+ const SCRIPT = 'resources/serviceworker-accessors.sw.js';
+
+ function assert_headers_not_seen_in_service_worker(frame) {
+ return new Promise((resolve, reject) => {
+ frame.contentWindow.fetch(SCOPE, {mode:'no-cors'});
+ frame.contentWindow.navigator.serviceWorker.addEventListener('message', e => {
+ assert_header_equals(e.data, {
+ "dest": null,
+ "mode": null,
+ "site": null,
+ "user": null
+ });
+ resolve();
+ });
+ });
+ }
+
+ promise_test(async function(t) {
+ const reg = await service_worker_unregister_and_register(t, SCRIPT, SCOPE);
+
+ t.add_cleanup(async () => {
+ if (reg)
+ await reg.unregister();
+ });
+
+ await wait_for_state(t, reg.installing, 'activated');
+
+ const frame = await with_iframe(SCOPE);
+ t.add_cleanup(async () => {
+ if (frame)
+ frame.remove();
+ });
+
+ // Trigger a fetch that will go through the service worker, and validate
+ // the visible headers.
+ await assert_headers_not_seen_in_service_worker(frame);
+ }, 'Sec-Fetch headers in Service Worker fetch handler.');
+</script>
diff --git a/test/wpt/tests/fetch/metadata/sharedworker.https.sub.html b/test/wpt/tests/fetch/metadata/sharedworker.https.sub.html
new file mode 100644
index 0000000..4df8582
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/sharedworker.https.sub.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+
+<link rel="author" href="mtrzos@google.com" title="Maciek Trzos">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<script>
+ let nonce = token();
+ let key = "sharedworker-same-origin" + nonce;
+
+ // TESTS //
+ if (window.Worker) {
+
+ // Same-Origin test
+ var sharedWorker = new SharedWorker('/fetch/metadata/resources/record-header.py?file=' + key);
+ sharedWorker.port.start();
+
+ sharedWorker.onerror = function(){
+ test_same_origin();
+ }
+ sharedWorker.port.onmessage = function(e) {
+ test_same_origin();
+ }
+ sharedWorker.port.postMessage("Ready");
+ }
+
+ function test_same_origin(){
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let expected = {"site":"same-origin", "user":"", "mode": "same-origin", "dest": "sharedworker"};
+
+ validate_expectations(key, expected)
+ .then(_ => resolve())
+ .catch(e => reject(e));
+ })
+ }, "Same-Origin sharedworker")
+ }
+</script>
+<body></body>
diff --git a/test/wpt/tests/fetch/metadata/style.https.sub.html b/test/wpt/tests/fetch/metadata/style.https.sub.html
new file mode 100644
index 0000000..a30d81d
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/style.https.sub.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<html>
+<link rel="author" href="mtrzos@google.com" title="Maciek Trzos">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<body></body>
+<script>
+ let nonce = token();
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = "style-same-origin" + nonce;
+
+ let e = document.createElement('link');
+ e.rel = "stylesheet";
+ e.href = "https://{{host}}:{{ports[https][0]}}/fetch/metadata/resources/record-header.py?file=" + key;
+ e.onload = e => {
+ let expected = {"site":"same-origin", "user":"", "mode": "no-cors", "dest": "style"};
+ validate_expectations(key, expected, "Same-Origin style")
+ .then(_ => resolve())
+ .catch(e => reject(e));
+ };
+
+ document.body.appendChild(e);
+ })
+ }, "Same-Origin style");
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = "style-same-site" + nonce;
+
+ let e = document.createElement('link');
+ e.rel = "stylesheet";
+ e.href = "https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/metadata/resources/record-header.py?file=" + key;
+ e.onload = e => {
+ let expected = {"site":"same-site", "user":"", "mode": "no-cors", "dest": "style"};
+ validate_expectations(key, expected, "Same-Site style")
+ .then(_ => resolve())
+ .catch(e => reject(e));
+ };
+
+ document.body.appendChild(e);
+ })
+ }, "Same-Site style");
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = "style-cross-site" + nonce;
+
+ let e = document.createElement('link');
+ e.rel = "stylesheet";
+ e.href = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/metadata/resources/record-header.py?file=" + key;
+ e.onload = e => {
+ let expected = {"site":"cross-site", "user":"", "mode": "no-cors", "dest": "style"};
+ validate_expectations(key, expected, "Cross-Site style")
+ .then(_ => resolve())
+ .catch(e => reject(e));
+ };
+
+ document.body.appendChild(e);
+ })
+ }, "Cross-Site style");
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = "style-same-origin-cors" + nonce;
+
+ let e = document.createElement('link');
+ e.rel = "stylesheet";
+ e.href = "https://{{host}}:{{ports[https][0]}}/fetch/metadata/resources/record-header.py?file=" + key;
+ e.crossOrigin = "anonymous";
+ e.onload = e => {
+ let expected = {"site":"same-origin", "user":"", "mode": "cors", "dest": "style"};
+ validate_expectations(key, expected, "Same-Origin, cors style")
+ .then(_ => resolve())
+ .catch(e => reject(e));
+ };
+
+ document.body.appendChild(e);
+ })
+ }, "Same-Origin, cors style");
+</script>
+</html>
+
diff --git a/test/wpt/tests/fetch/metadata/tools/README.md b/test/wpt/tests/fetch/metadata/tools/README.md
new file mode 100644
index 0000000..1c3bac2
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/README.md
@@ -0,0 +1,126 @@
+# Fetch Metadata test generation framework
+
+This directory defines a command-line tool for procedurally generating WPT
+tests.
+
+## Motivation
+
+Many features of the web platform involve the browser making one or more HTTP
+requests to remote servers. Only some aspects of these requests are specified
+within the standard that defines the relevant feature. Other aspects are
+specified by external standards which span the entire platform (e.g. [Fetch
+Metadata Request Headers](https://w3c.github.io/webappsec-fetch-metadata/)).
+
+This state of affairs makes it difficult to maintain test coverage for two
+reasons:
+
+- When a new feature introduces a new kind of web request, it must be verified
+ to integrate with every cross-cutting standard.
+- When a new cross-cutting standard is introduced, it must be verified to
+ integrate with every kind of web request.
+
+The tool in this directory attempts to reduce this tension. It allows
+maintainers to express instructions for making web requests in an abstract
+sense. These generic instructions can be reused by to produce a different suite
+of tests for each cross-cutting feature.
+
+When a new kind of request is proposed, a single generic template can be
+defined here. This will provide the maintainers of all cross-cutting features
+with clear instruction on how to extend their test suite with the new feature.
+
+Similarly, when a new cross-cutting feature is proposed, the authors can use
+this tool to build a test suite which spans the entire platform.
+
+## Build script
+
+To generate the Fetch Metadata tests, run `./wpt update-built --include fetch`
+in the root of the repository.
+
+## Configuration
+
+The test generation tool requires a YAML-formatted configuration file as its
+input. The file should define a dictionary with the following keys:
+
+- `templates` - a string describing the filesystem path from which template
+ files should be loaded
+- `output_directory` - a string describing the filesystem path where the
+ generated test files should be written
+- `cases` - a list of dictionaries describing how the test templates should be
+ expanded with individual subtests; each dictionary should have the following
+ keys:
+ - `all_subtests` - properties which should be defined for every expansion
+ - `common_axis` - a list of dictionaries
+ - `template_axes` - a dictionary relating template names to properties that
+ should be used when expanding that particular template
+
+Internally, the tool creates a set of "subtests" for each template. This set is
+the Cartesian product of the `common_axis` and the given template's entry in
+the `template_axes` dictionary. It uses this set of subtests to expand the
+template, creating an output file. Refer to the next section for a concrete
+example of how the expansion is performed.
+
+In general, the tool will output a single file for each template. However, the
+`filename_flags` attribute has special semantics. It is used to separate
+subtests for the same template file. This is intended to accommodate [the
+web-platform-test's filename-based
+conventions](https://web-platform-tests.org/writing-tests/file-names.html).
+
+For instance, when `.https` is present in a test file's name, the WPT test
+harness will load that test using the HTTPS protocol. Subtests which include
+the value `https` in the `filename_flags` property will be expanded using the
+appropriate template but written to a distinct file whose name includes
+`.https`.
+
+The generation tool requires that the configuration file references every
+template in the `templates` directory. Because templates and configuration
+files may be contributed by different people, this requirement ensures that
+configuration authors are aware of all available templates. Some templates may
+not be relevant for some features; in those cases, the configuration file can
+include an empty array for the template's entry in the `template_axes`
+dictionary (as in `template3.html` in the example which follows).
+
+## Expansion example
+
+In the following example configuration file, `a`, `b`, `s`, `w`, `x`, `y`, and
+`z` all represent associative arrays.
+
+```yaml
+templates: path/to/templates
+output_directory: path/to/output
+cases:
+ - every_subtest: s
+ common_axis: [a, b]
+ template_axes:
+ template1.html: [w]
+ template2.html: [x, y, z]
+ template3.html: []
+```
+
+When run with such a configuration file, the tool would generate two files,
+expanded with data as described below (where `(a, b)` represents the union of
+`a` and `b`):
+
+ template1.html: [(a, w), (b, w)]
+ template2.html: [(a, x), (b, x), (a, y), (b, y), (a, z), (b, z)]
+ template3.html: (zero tests; not expanded)
+
+## Design Considerations
+
+**Efficiency of generated output** The tool is capable of generating a large
+number of tests given a small amount of input. Naively structured, this could
+result in test suites which take large amount of time and computational
+resources to complete. The tool has been designed to help authors structure the
+generated output to reduce these resource requirements.
+
+**Literalness of generated output** Because the generated output is how most
+people will interact with the tests, it is important that it be approachable.
+This tool avoids outputting abstractions which would frustrate attempts to read
+the source code or step through its execution environment.
+
+**Simplicity** The test generation logic itself was written to be approachable.
+This makes it easier to anticipate how the tool will behave with new input, and
+it lowers the bar for others to contribute improvements.
+
+Non-goals include conciseness of template files (verbosity makes the potential
+expansions more predictable) and conciseness of generated output (verbosity
+aids in the interpretation of results).
diff --git a/test/wpt/tests/fetch/metadata/tools/fetch-metadata.conf.yml b/test/wpt/tests/fetch/metadata/tools/fetch-metadata.conf.yml
new file mode 100644
index 0000000..b277bcb
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/fetch-metadata.conf.yml
@@ -0,0 +1,806 @@
+---
+templates: templates
+output_directory: ../generated
+cases:
+ - all_subtests:
+ expected: NULL
+ filename_flags: []
+ common_axis:
+ - headerName: sec-fetch-site
+ origins: [httpOrigin]
+ description: Not sent to non-trustworthy same-origin destination
+ - headerName: sec-fetch-site
+ origins: [httpSameSite]
+ description: Not sent to non-trustworthy same-site destination
+ - headerName: sec-fetch-site
+ origins: [httpCrossSite]
+ description: Not sent to non-trustworthy cross-site destination
+ - headerName: sec-fetch-mode
+ origins: [httpOrigin]
+ description: Not sent to non-trustworthy same-origin destination
+ - headerName: sec-fetch-mode
+ origins: [httpSameSite]
+ description: Not sent to non-trustworthy same-site destination
+ - headerName: sec-fetch-mode
+ origins: [httpCrossSite]
+ description: Not sent to non-trustworthy cross-site destination
+ - headerName: sec-fetch-dest
+ origins: [httpOrigin]
+ description: Not sent to non-trustworthy same-origin destination
+ - headerName: sec-fetch-dest
+ origins: [httpSameSite]
+ description: Not sent to non-trustworthy same-site destination
+ - headerName: sec-fetch-dest
+ origins: [httpCrossSite]
+ description: Not sent to non-trustworthy cross-site destination
+ - headerName: sec-fetch-user
+ origins: [httpOrigin]
+ description: Not sent to non-trustworthy same-origin destination
+ - headerName: sec-fetch-user
+ origins: [httpSameSite]
+ description: Not sent to non-trustworthy same-site destination
+ - headerName: sec-fetch-user
+ origins: [httpCrossSite]
+ description: Not sent to non-trustworthy cross-site destination
+ template_axes:
+ # Unused
+ appcache-manifest.sub.https.html: []
+ # The `audioWorklet` interface is only available in secure contexts
+ # https://webaudio.github.io/web-audio-api/#BaseAudioContext
+ audioworklet.https.sub.html: []
+ # Service workers are only available in secure context
+ fetch-via-serviceworker.https.sub.html: []
+ # Service workers are only available in secure context
+ serviceworker.https.sub.html: []
+
+ css-images.sub.html:
+ - filename_flags: [tentative]
+ css-font-face.sub.html:
+ - filename_flags: [tentative]
+ element-a.sub.html: [{}]
+ element-area.sub.html: [{}]
+ element-audio.sub.html: [{}]
+ element-embed.sub.html: [{}]
+ element-frame.sub.html: [{}]
+ element-iframe.sub.html: [{}]
+ element-img.sub.html:
+ - sourceAttr: src
+ - sourceAttr: srcset
+ element-img-environment-change.sub.html: [{}]
+ element-input-image.sub.html: [{}]
+ element-link-icon.sub.html: [{}]
+ element-link-prefetch.optional.sub.html: [{}]
+ element-meta-refresh.optional.sub.html: [{}]
+ element-picture.sub.html: [{}]
+ element-script.sub.html:
+ - {}
+ - elementAttrs: { type: module }
+ element-video.sub.html: [{}]
+ element-video-poster.sub.html: [{}]
+ fetch.sub.html: [{}]
+ form-submission.sub.html:
+ - method: GET
+ - method: POST
+ header-link.sub.html:
+ - rel: icon
+ - rel: stylesheet
+ header-refresh.optional.sub.html: [{}]
+ window-location.sub.html: [{}]
+ script-module-import-dynamic.sub.html: [{}]
+ script-module-import-static.sub.html: [{}]
+ svg-image.sub.html: [{}]
+ window-history.sub.html: [{}]
+ worker-dedicated-importscripts.sub.html: [{}]
+ worker-dedicated-constructor.sub.html: [{}]
+
+ # Sec-Fetch-Site - direct requests
+ - all_subtests:
+ headerName: sec-fetch-site
+ filename_flags: [https]
+ common_axis:
+ - description: Same origin
+ origins: [httpsOrigin]
+ expected: same-origin
+ - description: Cross-site
+ origins: [httpsCrossSite]
+ expected: cross-site
+ - description: Same site
+ origins: [httpsSameSite]
+ expected: same-site
+ template_axes:
+ # Unused
+ # - the request mode of all "classic" worker scripts is set to
+ # "same-origin"
+ # https://html.spec.whatwg.org/#fetch-a-classic-worker-script
+ # - the request mode of all "top-level "module" worker scripts is set to
+ # "same-origin":
+ # https://html.spec.whatwg.org/#fetch-a-single-module-script
+ worker-dedicated-constructor.sub.html: []
+
+ appcache-manifest.sub.https.html: [{}]
+ audioworklet.https.sub.html: [{}]
+ css-images.sub.html:
+ - filename_flags: [tentative]
+ css-font-face.sub.html:
+ - filename_flags: [tentative]
+ element-a.sub.html: [{}]
+ element-area.sub.html: [{}]
+ element-audio.sub.html: [{}]
+ element-embed.sub.html: [{}]
+ element-frame.sub.html: [{}]
+ element-iframe.sub.html: [{}]
+ element-img.sub.html:
+ - sourceAttr: src
+ - sourceAttr: srcset
+ element-img-environment-change.sub.html: [{}]
+ element-input-image.sub.html: [{}]
+ element-link-icon.sub.html: [{}]
+ element-link-prefetch.optional.sub.html: [{}]
+ element-meta-refresh.optional.sub.html: [{}]
+ element-picture.sub.html: [{}]
+ element-script.sub.html:
+ - {}
+ - elementAttrs: { type: module }
+ element-video.sub.html: [{}]
+ element-video-poster.sub.html: [{}]
+ fetch.sub.html: [{ init: { mode: no-cors } }]
+ fetch-via-serviceworker.https.sub.html: [{ init: { mode: no-cors } }]
+ form-submission.sub.html:
+ - method: GET
+ - method: POST
+ header-link.sub.html:
+ - rel: icon
+ - rel: stylesheet
+ header-refresh.optional.sub.html: [{}]
+ window-location.sub.html: [{}]
+ script-module-import-dynamic.sub.html: [{}]
+ script-module-import-static.sub.html: [{}]
+ serviceworker.https.sub.html: [{}]
+ svg-image.sub.html: [{}]
+ window-history.sub.html: [{}]
+ worker-dedicated-importscripts.sub.html: [{}]
+
+ # Sec-Fetch-Site - redirection from HTTP
+ - all_subtests:
+ headerName: sec-fetch-site
+ filename_flags: []
+ common_axis:
+ - description: HTTPS downgrade (header not sent)
+ origins: [httpsOrigin, httpOrigin]
+ expected: NULL
+ - description: HTTPS upgrade
+ origins: [httpOrigin, httpsOrigin]
+ expected: cross-site
+ - description: HTTPS downgrade-upgrade
+ origins: [httpsOrigin, httpOrigin, httpsOrigin]
+ expected: cross-site
+ template_axes:
+ # Unused
+ # The `audioWorklet` interface is only available in secure contexts
+ # https://webaudio.github.io/web-audio-api/#BaseAudioContext
+ audioworklet.https.sub.html: []
+ # Service workers are only available in secure context
+ fetch-via-serviceworker.https.sub.html: []
+ # Service workers' redirect mode is "error"
+ serviceworker.https.sub.html: []
+ # Interstitial locations in an HTTP redirect chain are not added to the
+ # session history, so these requests cannot be initiated using the
+ # History API.
+ window-history.sub.html: []
+ # Unused
+ # - the request mode of all "classic" worker scripts is set to
+ # "same-origin"
+ # https://html.spec.whatwg.org/#fetch-a-classic-worker-script
+ # - the request mode of all "top-level "module" worker scripts is set to
+ # "same-origin":
+ # https://html.spec.whatwg.org/#fetch-a-single-module-script
+ worker-dedicated-constructor.sub.html: []
+
+ appcache-manifest.sub.https.html: [{}]
+ css-images.sub.html:
+ - filename_flags: [tentative]
+ css-font-face.sub.html:
+ - filename_flags: [tentative]
+ element-a.sub.html: [{}]
+ element-area.sub.html: [{}]
+ element-audio.sub.html: [{}]
+ element-embed.sub.html: [{}]
+ element-frame.sub.html: [{}]
+ element-iframe.sub.html: [{}]
+ element-img.sub.html:
+ - sourceAttr: src
+ - sourceAttr: srcset
+ element-img-environment-change.sub.html: [{}]
+ element-input-image.sub.html: [{}]
+ element-link-icon.sub.html: [{}]
+ element-link-prefetch.optional.sub.html: [{}]
+ element-meta-refresh.optional.sub.html: [{}]
+ element-picture.sub.html: [{}]
+ element-script.sub.html:
+ - {}
+ - elementAttrs: { type: module }
+ element-video.sub.html: [{}]
+ element-video-poster.sub.html: [{}]
+ fetch.sub.html: [{}]
+ form-submission.sub.html:
+ - method: GET
+ - method: POST
+ header-link.sub.html:
+ - rel: icon
+ - rel: stylesheet
+ header-refresh.optional.sub.html: [{}]
+ window-location.sub.html: [{}]
+ script-module-import-dynamic.sub.html: [{}]
+ script-module-import-static.sub.html: [{}]
+ svg-image.sub.html: [{}]
+ worker-dedicated-importscripts.sub.html: [{}]
+
+ # Sec-Fetch-Site - redirection from HTTPS
+ - all_subtests:
+ headerName: sec-fetch-site
+ filename_flags: [https]
+ common_axis:
+ - description: Same-Origin -> Cross-Site -> Same-Origin redirect
+ origins: [httpsOrigin, httpsCrossSite, httpsOrigin]
+ expected: cross-site
+ - description: Same-Origin -> Same-Site -> Same-Origin redirect
+ origins: [httpsOrigin, httpsSameSite, httpsOrigin]
+ expected: same-site
+ - description: Cross-Site -> Same Origin
+ origins: [httpsCrossSite, httpsOrigin]
+ expected: cross-site
+ - description: Cross-Site -> Same-Site
+ origins: [httpsCrossSite, httpsSameSite]
+ expected: cross-site
+ - description: Cross-Site -> Cross-Site
+ origins: [httpsCrossSite, httpsCrossSite]
+ expected: cross-site
+ - description: Same-Origin -> Same Origin
+ origins: [httpsOrigin, httpsOrigin]
+ expected: same-origin
+ - description: Same-Origin -> Same-Site
+ origins: [httpsOrigin, httpsSameSite]
+ expected: same-site
+ - description: Same-Origin -> Cross-Site
+ origins: [httpsOrigin, httpsCrossSite]
+ expected: cross-site
+ - description: Same-Site -> Same Origin
+ origins: [httpsSameSite, httpsOrigin]
+ expected: same-site
+ - description: Same-Site -> Same-Site
+ origins: [httpsSameSite, httpsSameSite]
+ expected: same-site
+ - description: Same-Site -> Cross-Site
+ origins: [httpsSameSite, httpsCrossSite]
+ expected: cross-site
+ template_axes:
+ # Service Workers' redirect mode is "error"
+ serviceworker.https.sub.html: []
+ # Interstitial locations in an HTTP redirect chain are not added to the
+ # session history, so these requests cannot be initiated using the
+ # History API.
+ window-history.sub.html: []
+ # Unused
+ # - the request mode of all "classic" worker scripts is set to
+ # "same-origin"
+ # https://html.spec.whatwg.org/#fetch-a-classic-worker-script
+ # - the request mode of all "top-level "module" worker scripts is set to
+ # "same-origin":
+ # https://html.spec.whatwg.org/#fetch-a-single-module-script
+ worker-dedicated-constructor.sub.html: []
+
+ appcache-manifest.sub.https.html: [{}]
+ audioworklet.https.sub.html: [{}]
+ css-images.sub.html:
+ - filename_flags: [tentative]
+ css-font-face.sub.html:
+ - filename_flags: [tentative]
+ element-a.sub.html: [{}]
+ element-area.sub.html: [{}]
+ element-audio.sub.html: [{}]
+ element-embed.sub.html: [{}]
+ element-frame.sub.html: [{}]
+ element-iframe.sub.html: [{}]
+ element-img.sub.html:
+ - sourceAttr: src
+ - sourceAttr: srcset
+ element-img-environment-change.sub.html: [{}]
+ element-input-image.sub.html: [{}]
+ element-link-icon.sub.html: [{}]
+ element-link-prefetch.optional.sub.html: [{}]
+ element-meta-refresh.optional.sub.html: [{}]
+ element-picture.sub.html: [{}]
+ element-script.sub.html:
+ - {}
+ - elementAttrs: { type: module }
+ element-video.sub.html: [{}]
+ element-video-poster.sub.html: [{}]
+ fetch.sub.html: [{ init: { mode: no-cors } }]
+ fetch-via-serviceworker.https.sub.html: [{ init: { mode: no-cors } }]
+ form-submission.sub.html:
+ - method: GET
+ - method: POST
+ header-link.sub.html:
+ - rel: icon
+ - rel: stylesheet
+ header-refresh.optional.sub.html: [{}]
+ window-location.sub.html: [{}]
+ script-module-import-dynamic.sub.html: [{}]
+ script-module-import-static.sub.html: [{}]
+ svg-image.sub.html: [{}]
+ worker-dedicated-importscripts.sub.html: [{}]
+
+ # Sec-Fetch-Site - redirection with mixed content
+ # These tests verify the effect that redirection has on the request's "site".
+ # The initial request must be made to a resource that is "same-site" with its
+ # origin. This avoids false positives because if the request were made to a
+ # cross-site resource, the value of "cross-site" would be assigned regardless
+ # of the subseqent redirection.
+ #
+ # Because these conditions necessarily warrant mixed content, only templates
+ # which can be configured to allow mixed content [1] can be used.
+ #
+ # [1] https://w3c.github.io/webappsec-mixed-content/#should-block-fetch
+
+ - common_axis:
+ - description: HTTPS downgrade-upgrade
+ headerName: sec-fetch-site
+ origins: [httpsOrigin, httpOrigin, httpsOrigin]
+ expected: cross-site
+ filename_flags: [https]
+ template_axes:
+ # Mixed Content considers only a small subset of requests as
+ # "optionally-blockable." These are the only requests that can be tested
+ # for the "downgrade-upgrade" scenario, so all other templates must be
+ # explicitly ignored.
+ audioworklet.https.sub.html: []
+ css-font-face.sub.html: []
+ element-embed.sub.html: []
+ element-frame.sub.html: []
+ element-iframe.sub.html: []
+ element-img-environment-change.sub.html: []
+ element-link-icon.sub.html: []
+ element-link-prefetch.optional.sub.html: []
+ element-picture.sub.html: []
+ element-script.sub.html: []
+ fetch.sub.html: []
+ fetch-via-serviceworker.https.sub.html: []
+ header-link.sub.html: []
+ script-module-import-static.sub.html: []
+ script-module-import-dynamic.sub.html: []
+ # Service Workers' redirect mode is "error"
+ serviceworker.https.sub.html: []
+ # Interstitial locations in an HTTP redirect chain are not added to the
+ # session history, so these requests cannot be initiated using the
+ # History API.
+ window-history.sub.html: []
+ worker-dedicated-constructor.sub.html: []
+ worker-dedicated-importscripts.sub.html: []
+ # Avoid duplicate subtest for 'sec-fetch-site - HTTPS downgrade-upgrade'
+ appcache-manifest.sub.https.html: []
+ css-images.sub.html:
+ - filename_flags: [tentative]
+ element-a.sub.html: [{}]
+ element-area.sub.html: [{}]
+ element-audio.sub.html: [{}]
+ element-img.sub.html:
+ # srcset omitted because it is not "optionally-blockable"
+ # https://w3c.github.io/webappsec-mixed-content/#category-optionally-blockable
+ - sourceAttr: src
+ element-input-image.sub.html: [{}]
+ element-meta-refresh.optional.sub.html: [{}]
+ element-video.sub.html: [{}]
+ element-video-poster.sub.html: [{}]
+ form-submission.sub.html:
+ - method: GET
+ - method: POST
+ header-refresh.optional.sub.html: [{}]
+ svg-image.sub.html: [{}]
+ window-location.sub.html: [{}]
+
+ # Sec-Fetch-Mode
+ # These tests are served over HTTPS so the induced requests will be both
+ # same-origin with the document [1] and a potentially-trustworthy URL [2].
+ #
+ # [1] https://html.spec.whatwg.org/multipage/origin.html#same-origin
+ # [2] https://w3c.github.io/webappsec-secure-contexts/#potentially-trustworthy-url
+ - common_axis:
+ - headerName: sec-fetch-mode
+ filename_flags: [https]
+ origins: []
+ template_axes:
+ appcache-manifest.sub.https.html:
+ - expected: no-cors
+ audioworklet.https.sub.html:
+ # https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-single-module-script
+ - expected: cors
+ css-images.sub.html:
+ - expected: no-cors
+ filename_flags: [tentative]
+ css-font-face.sub.html:
+ - expected: cors
+ filename_flags: [tentative]
+ element-a.sub.html:
+ - expected: navigate
+ # https://html.spec.whatwg.org/multipage/links.html#downloading-hyperlinks
+ - elementAttrs: {download: ''}
+ expected: no-cors
+ element-area.sub.html:
+ - expected: navigate
+ # https://html.spec.whatwg.org/multipage/links.html#downloading-hyperlinks
+ - elementAttrs: {download: ''}
+ expected: no-cors
+ element-audio.sub.html:
+ - expected: no-cors
+ - expected: cors
+ elementAttrs: { crossorigin: '' }
+ - expected: cors
+ elementAttrs: { crossorigin: anonymous }
+ - expected: cors
+ elementAttrs: { crossorigin: use-credentials }
+ element-embed.sub.html:
+ - expected: no-cors
+ element-frame.sub.html:
+ - expected: navigate
+ element-iframe.sub.html:
+ - expected: navigate
+ element-img.sub.html:
+ - sourceAttr: src
+ expected: no-cors
+ - sourceAttr: src
+ expected: cors
+ elementAttrs: { crossorigin: '' }
+ - sourceAttr: src
+ expected: cors
+ elementAttrs: { crossorigin: anonymous }
+ - sourceAttr: src
+ expected: cors
+ elementAttrs: { crossorigin: use-credentials }
+ - sourceAttr: srcset
+ expected: no-cors
+ - sourceAttr: srcset
+ expected: cors
+ elementAttrs: { crossorigin: '' }
+ - sourceAttr: srcset
+ expected: cors
+ elementAttrs: { crossorigin: anonymous }
+ - sourceAttr: srcset
+ expected: cors
+ elementAttrs: { crossorigin: use-credentials }
+ element-img-environment-change.sub.html:
+ - expected: no-cors
+ - expected: cors
+ elementAttrs: { crossorigin: '' }
+ - expected: cors
+ elementAttrs: { crossorigin: anonymous }
+ - expected: cors
+ elementAttrs: { crossorigin: use-credentials }
+ element-input-image.sub.html:
+ - expected: no-cors
+ element-link-icon.sub.html:
+ - expected: no-cors
+ - expected: cors
+ elementAttrs: { crossorigin: '' }
+ - expected: cors
+ elementAttrs: { crossorigin: anonymous }
+ - expected: cors
+ elementAttrs: { crossorigin: use-credentials }
+ element-link-prefetch.optional.sub.html:
+ - expected: no-cors
+ - expected: cors
+ elementAttrs: { crossorigin: '' }
+ - expected: cors
+ elementAttrs: { crossorigin: anonymous }
+ - expected: cors
+ elementAttrs: { crossorigin: use-credentials }
+ element-meta-refresh.optional.sub.html:
+ - expected: navigate
+ element-picture.sub.html:
+ - expected: no-cors
+ - expected: cors
+ elementAttrs: { crossorigin: '' }
+ - expected: cors
+ elementAttrs: { crossorigin: anonymous }
+ - expected: cors
+ elementAttrs: { crossorigin: use-credentials }
+ element-script.sub.html:
+ - expected: no-cors
+ - expected: cors
+ elementAttrs: { type: module }
+ - expected: cors
+ elementAttrs: { crossorigin: '' }
+ - expected: cors
+ elementAttrs: { crossorigin: anonymous }
+ - expected: cors
+ elementAttrs: { crossorigin: use-credentials }
+ element-video.sub.html:
+ - expected: no-cors
+ - expected: cors
+ elementAttrs: { crossorigin: '' }
+ - expected: cors
+ elementAttrs: { crossorigin: anonymous }
+ - expected: cors
+ elementAttrs: { crossorigin: use-credentials }
+ element-video-poster.sub.html:
+ - expected: no-cors
+ fetch.sub.html:
+ - expected: cors
+ - expected: cors
+ init: { mode: cors }
+ - expected: no-cors
+ init: { mode: no-cors }
+ - expected: same-origin
+ init: { mode: same-origin }
+ fetch-via-serviceworker.https.sub.html:
+ - expected: cors
+ - expected: cors
+ init: { mode: cors }
+ - expected: no-cors
+ init: { mode: no-cors }
+ - expected: same-origin
+ init: { mode: same-origin }
+ form-submission.sub.html:
+ - method: GET
+ expected: navigate
+ - method: POST
+ expected: navigate
+ header-link.sub.html:
+ - rel: icon
+ expected: no-cors
+ - rel: stylesheet
+ expected: no-cors
+ header-refresh.optional.sub.html:
+ - expected: navigate
+ window-history.sub.html:
+ - expected: navigate
+ window-location.sub.html:
+ - expected: navigate
+ script-module-import-dynamic.sub.html:
+ - expected: cors
+ script-module-import-static.sub.html:
+ - expected: cors
+ # https://svgwg.org/svg2-draft/linking.html#processingURL-fetch
+ svg-image.sub.html:
+ - expected: no-cors
+ - expected: cors
+ elementAttrs: { crossorigin: '' }
+ - expected: cors
+ elementAttrs: { crossorigin: anonymous }
+ - expected: cors
+ elementAttrs: { crossorigin: use-credentials }
+ serviceworker.https.sub.html:
+ - expected: same-origin
+ options: { type: 'classic' }
+ # https://github.com/whatwg/html/pull/5875
+ - expected: same-origin
+ worker-dedicated-constructor.sub.html:
+ - expected: same-origin
+ - options: { type: module }
+ expected: same-origin
+ worker-dedicated-importscripts.sub.html:
+ - expected: no-cors
+
+ # Sec-Fetch-Dest
+ - common_axis:
+ - headerName: sec-fetch-dest
+ filename_flags: [https]
+ origins: []
+ template_axes:
+ appcache-manifest.sub.https.html:
+ - expected: empty
+ audioworklet.https.sub.html:
+ # https://github.com/WebAudio/web-audio-api/issues/2203
+ - expected: audioworklet
+ css-images.sub.html:
+ - expected: image
+ filename_flags: [tentative]
+ css-font-face.sub.html:
+ - expected: font
+ filename_flags: [tentative]
+ element-a.sub.html:
+ - expected: document
+ # https://html.spec.whatwg.org/multipage/links.html#downloading-hyperlinks
+ - elementAttrs: {download: ''}
+ expected: empty
+ element-area.sub.html:
+ - expected: document
+ # https://html.spec.whatwg.org/multipage/links.html#downloading-hyperlinks
+ - elementAttrs: {download: ''}
+ expected: empty
+ element-audio.sub.html:
+ - expected: audio
+ element-embed.sub.html:
+ - expected: embed
+ element-frame.sub.html:
+ # https://github.com/whatwg/html/pull/4976
+ - expected: frame
+ element-iframe.sub.html:
+ # https://github.com/whatwg/html/pull/4976
+ - expected: iframe
+ element-img.sub.html:
+ - sourceAttr: src
+ expected: image
+ - sourceAttr: srcset
+ expected: image
+ element-img-environment-change.sub.html:
+ - expected: image
+ element-input-image.sub.html:
+ - expected: image
+ element-link-icon.sub.html:
+ - expected: empty
+ element-link-prefetch.optional.sub.html:
+ - expected: empty
+ - elementAttrs: { as: audio }
+ expected: audio
+ - elementAttrs: { as: document }
+ expected: document
+ - elementAttrs: { as: embed }
+ expected: embed
+ - elementAttrs: { as: fetch }
+ expected: fetch
+ - elementAttrs: { as: font }
+ expected: font
+ - elementAttrs: { as: image }
+ expected: image
+ - elementAttrs: { as: object }
+ expected: object
+ - elementAttrs: { as: script }
+ expected: script
+ - elementAttrs: { as: style }
+ expected: style
+ - elementAttrs: { as: track }
+ expected: track
+ - elementAttrs: { as: video }
+ expected: video
+ - elementAttrs: { as: worker }
+ expected: worker
+ element-meta-refresh.optional.sub.html:
+ - expected: document
+ element-picture.sub.html:
+ - expected: image
+ element-script.sub.html:
+ - expected: script
+ element-video.sub.html:
+ - expected: video
+ element-video-poster.sub.html:
+ - expected: image
+ fetch.sub.html:
+ - expected: empty
+ fetch-via-serviceworker.https.sub.html:
+ - expected: empty
+ form-submission.sub.html:
+ - method: GET
+ expected: document
+ - method: POST
+ expected: document
+ header-link.sub.html:
+ - rel: icon
+ expected: empty
+ - rel: stylesheet
+ filename_flags: [tentative]
+ expected: style
+ header-refresh.optional.sub.html:
+ - expected: document
+ window-history.sub.html:
+ - expected: document
+ window-location.sub.html:
+ - expected: document
+ script-module-import-dynamic.sub.html:
+ - expected: script
+ script-module-import-static.sub.html:
+ - expected: script
+ serviceworker.https.sub.html:
+ - expected: serviceworker
+ # Implemented as "image" in Chromium and Firefox, but specified as
+ # "empty"
+ # https://github.com/w3c/svgwg/issues/782
+ svg-image.sub.html:
+ - expected: empty
+ worker-dedicated-constructor.sub.html:
+ - expected: worker
+ - options: { type: module }
+ expected: worker
+ worker-dedicated-importscripts.sub.html:
+ - expected: script
+
+ # Sec-Fetch-User
+ - common_axis:
+ - headerName: sec-fetch-user
+ filename_flags: [https]
+ origins: []
+ template_axes:
+ appcache-manifest.sub.https.html:
+ - expected: NULL
+ audioworklet.https.sub.html:
+ - expected: NULL
+ css-images.sub.html:
+ - expected: NULL
+ filename_flags: [tentative]
+ css-font-face.sub.html:
+ - expected: NULL
+ filename_flags: [tentative]
+ element-a.sub.html:
+ - expected: NULL
+ - userActivated: TRUE
+ expected: ?1
+ element-area.sub.html:
+ - expected: NULL
+ - userActivated: TRUE
+ expected: ?1
+ element-audio.sub.html:
+ - expected: NULL
+ element-embed.sub.html:
+ - expected: NULL
+ element-frame.sub.html:
+ - expected: NULL
+ - userActivated: TRUE
+ expected: ?1
+ element-iframe.sub.html:
+ - expected: NULL
+ - userActivated: TRUE
+ expected: ?1
+ element-img.sub.html:
+ - sourceAttr: src
+ expected: NULL
+ - sourceAttr: srcset
+ expected: NULL
+ element-img-environment-change.sub.html:
+ - expected: NULL
+ element-input-image.sub.html:
+ - expected: NULL
+ element-link-icon.sub.html:
+ - expected: NULL
+ element-link-prefetch.optional.sub.html:
+ - expected: NULL
+ element-meta-refresh.optional.sub.html:
+ - expected: NULL
+ element-picture.sub.html:
+ - expected: NULL
+ element-script.sub.html:
+ - expected: NULL
+ element-video.sub.html:
+ - expected: NULL
+ element-video-poster.sub.html:
+ - expected: NULL
+ fetch.sub.html:
+ - expected: NULL
+ fetch-via-serviceworker.https.sub.html:
+ - expected: NULL
+ form-submission.sub.html:
+ - method: GET
+ expected: NULL
+ - method: GET
+ userActivated: TRUE
+ expected: ?1
+ - method: POST
+ expected: NULL
+ - method: POST
+ userActivated: TRUE
+ expected: ?1
+ header-link.sub.html:
+ - rel: icon
+ expected: NULL
+ - rel: stylesheet
+ expected: NULL
+ header-refresh.optional.sub.html:
+ - expected: NULL
+ window-history.sub.html:
+ - expected: NULL
+ window-location.sub.html:
+ - expected: NULL
+ - userActivated: TRUE
+ expected: ?1
+ script-module-import-dynamic.sub.html:
+ - expected: NULL
+ script-module-import-static.sub.html:
+ - expected: NULL
+ serviceworker.https.sub.html:
+ - expected: NULL
+ svg-image.sub.html:
+ - expected: NULL
+ worker-dedicated-constructor.sub.html:
+ - expected: NULL
+ - options: { type: module }
+ expected: NULL
+ worker-dedicated-importscripts.sub.html:
+ - expected: NULL
diff --git a/test/wpt/tests/fetch/metadata/tools/generate.py b/test/wpt/tests/fetch/metadata/tools/generate.py
new file mode 100644
index 0000000..fa850c8
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/generate.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python3
+
+import itertools
+import os
+
+import jinja2
+import yaml
+
+HERE = os.path.abspath(os.path.dirname(__file__))
+PROJECT_ROOT = os.path.join(HERE, '..', '..', '..')
+
+def find_templates(starting_directory):
+ for directory, subdirectories, file_names in os.walk(starting_directory):
+ for file_name in file_names:
+ if file_name.startswith('.'):
+ continue
+ yield file_name, os.path.join(directory, file_name)
+
+def test_name(directory, template_name, subtest_flags):
+ '''
+ Create a test name based on a template and the WPT file name flags [1]
+ required for a given subtest. This name is used to determine how subtests
+ may be grouped together. In order to promote grouping, the combination uses
+ a few aspects of how file name flags are interpreted:
+
+ - repeated flags have no effect, so duplicates are removed
+ - flag sequence does not matter, so flags are consistently sorted
+
+ directory | template_name | subtest_flags | result
+ ----------|------------------|-----------------|-------
+ cors | image.html | [] | cors/image.html
+ cors | image.https.html | [] | cors/image.https.html
+ cors | image.html | [https] | cors/image.https.html
+ cors | image.https.html | [https] | cors/image.https.html
+ cors | image.https.html | [https] | cors/image.https.html
+ cors | image.sub.html | [https] | cors/image.https.sub.html
+ cors | image.https.html | [sub] | cors/image.https.sub.html
+
+ [1] docs/writing-tests/file-names.md
+ '''
+ template_name_parts = template_name.split('.')
+ flags = set(subtest_flags) | set(template_name_parts[1:-1])
+ test_name_parts = (
+ [template_name_parts[0]] +
+ sorted(flags) +
+ [template_name_parts[-1]]
+ )
+ return os.path.join(directory, '.'.join(test_name_parts))
+
+def merge(a, b):
+ if type(a) != type(b):
+ raise Exception('Cannot merge disparate types')
+ if type(a) == list:
+ return a + b
+ if type(a) == dict:
+ merged = {}
+
+ for key in a:
+ if key in b:
+ merged[key] = merge(a[key], b[key])
+ else:
+ merged[key] = a[key]
+
+ for key in b:
+ if not key in a:
+ merged[key] = b[key]
+
+ return merged
+
+ raise Exception('Cannot merge {} type'.format(type(a).__name__))
+
+def product(a, b):
+ '''
+ Given two lists of objects, compute their Cartesian product by merging the
+ elements together. For example,
+
+ product(
+ [{'a': 1}, {'b': 2}],
+ [{'c': 3}, {'d': 4}, {'e': 5}]
+ )
+
+ returns the following list:
+
+ [
+ {'a': 1, 'c': 3},
+ {'a': 1, 'd': 4},
+ {'a': 1, 'e': 5},
+ {'b': 2, 'c': 3},
+ {'b': 2, 'd': 4},
+ {'b': 2, 'e': 5}
+ ]
+ '''
+ result = []
+
+ for a_object in a:
+ for b_object in b:
+ result.append(merge(a_object, b_object))
+
+ return result
+
+def make_provenance(project_root, cases, template):
+ return '\n'.join([
+ 'This test was procedurally generated. Please do not modify it directly.',
+ 'Sources:',
+ '- {}'.format(os.path.relpath(cases, project_root)),
+ '- {}'.format(os.path.relpath(template, project_root))
+ ])
+
+def collection_filter(obj, title):
+ if not obj:
+ return 'no {}'.format(title)
+
+ members = []
+ for name, value in obj.items():
+ if value == '':
+ members.append(name)
+ else:
+ members.append('{}={}'.format(name, value))
+
+ return '{}: {}'.format(title, ', '.join(members))
+
+def pad_filter(value, side, padding):
+ if not value:
+ return ''
+ if side == 'start':
+ return padding + value
+
+ return value + padding
+
+def main(config_file):
+ with open(config_file, 'r') as handle:
+ config = yaml.safe_load(handle.read())
+
+ templates_directory = os.path.normpath(
+ os.path.join(os.path.dirname(config_file), config['templates'])
+ )
+
+ environment = jinja2.Environment(
+ variable_start_string='[%',
+ variable_end_string='%]'
+ )
+ environment.filters['collection'] = collection_filter
+ environment.filters['pad'] = pad_filter
+ templates = {}
+ subtests = {}
+
+ for template_name, path in find_templates(templates_directory):
+ subtests[template_name] = []
+ with open(path, 'r') as handle:
+ templates[template_name] = environment.from_string(handle.read())
+
+ for case in config['cases']:
+ unused_templates = set(templates) - set(case['template_axes'])
+
+ # This warning is intended to help authors avoid mistakenly omitting
+ # templates. It can be silenced by extending the`template_axes`
+ # dictionary with an empty list for templates which are intentionally
+ # unused.
+ if unused_templates:
+ print(
+ 'Warning: case does not reference the following templates:'
+ )
+ print('\n'.join('- {}'.format(name) for name in unused_templates))
+
+ common_axis = product(
+ case['common_axis'], [case.get('all_subtests', {})]
+ )
+
+ for template_name, template_axis in case['template_axes'].items():
+ subtests[template_name].extend(product(common_axis, template_axis))
+
+ for template_name, template in templates.items():
+ provenance = make_provenance(
+ PROJECT_ROOT,
+ config_file,
+ os.path.join(templates_directory, template_name)
+ )
+ get_filename = lambda subtest: test_name(
+ config['output_directory'],
+ template_name,
+ subtest['filename_flags']
+ )
+ subtests_by_filename = itertools.groupby(
+ sorted(subtests[template_name], key=get_filename),
+ key=get_filename
+ )
+ for filename, some_subtests in subtests_by_filename:
+ with open(filename, 'w') as handle:
+ handle.write(templates[template_name].render(
+ subtests=list(some_subtests),
+ provenance=provenance
+ ) + '\n')
+
+if __name__ == '__main__':
+ main('fetch-metadata.conf.yml')
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/appcache-manifest.sub.https.html b/test/wpt/tests/fetch/metadata/tools/templates/appcache-manifest.sub.https.html
new file mode 100644
index 0000000..0dfc084
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/appcache-manifest.sub.https.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for Appcache manifest</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url) {
+ const iframe = document.createElement('iframe');
+ iframe.src =
+ '/fetch/metadata/resources/appcache-iframe.sub.html?manifest=' + encodeURIComponent(url);
+
+ return new Promise((resolve) => {
+ addEventListener('message', function onMessage(event) {
+ if (event.source !== iframe.contentWindow) {
+ return;
+ }
+ removeEventListener('message', onMessage);
+ resolve(event.data);
+ });
+
+ document.body.appendChild(iframe);
+ })
+ .then((message) => {
+ if (message !== 'okay') {
+ throw message;
+ }
+ })
+ .then(() => iframe.remove());
+ }
+
+ {%- for subtest in subtests %}
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ assert_implements_optional(
+ !!window.applicationCache, 'Application Cache supported.'
+ );
+
+ induceRequest(makeRequestURL(key, [% subtest.origins %]))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ })
+ .then(() => t.done(), t.step_func((error) => { throw error; }));
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/audioworklet.https.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/audioworklet.https.sub.html
new file mode 100644
index 0000000..7be309c
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/audioworklet.https.sub.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for AudioWorklet module</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test) {
+ return test_driver.bless(
+ 'Enable WebAudio playback',
+ () => {
+ const audioContext = new AudioContext();
+
+ test.add_cleanup(() => audioContext.close());
+
+ return audioContext.audioWorklet.addModule(url);
+ }
+ );
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [% subtest.origins %], {mime: 'text/javascript'}),
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/css-font-face.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/css-font-face.sub.html
new file mode 100644
index 0000000..94b33f4
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/css-font-face.sub.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for CSS font-face</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ let count = 0;
+
+ function induceRequest(t, url) {
+ const id = `el-${count += 1}`;
+ const style = document.createElement('style');
+ style.appendChild(document.createTextNode(`
+ @font-face {
+ font-family: wpt-font-family${id};
+ src: url(${url});
+ }
+ #el-${id} {
+ font-family: wpt-font-family${id};
+ }
+ `));
+ const div = document.createElement('div');
+ div.setAttribute('id', 'el-' + id);
+ div.appendChild(style);
+ div.appendChild(document.createTextNode('x'));
+ document.body.appendChild(div);
+
+ t.add_cleanup(() => div.remove());
+
+ return document.fonts.ready;
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, [% subtest.origins %]))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/css-images.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/css-images.sub.html
new file mode 100644
index 0000000..e394f9f
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/css-images.sub.html
@@ -0,0 +1,137 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ {%- if subtests|length > 10 %}
+ <meta name="timeout" content="long">
+ {%- endif %}
+ <title>HTTP headers on request for CSS image-accepting properties</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ /**
+ * The subtests in this file use an iframe to induce requests for CSS
+ * resources because an iframe's `onload` event is the most direct and
+ * generic mechanism to detect loading of CSS resources. As an optimization,
+ * the subtests share the same iframe and document.
+ */
+ const declarations = [];
+ const iframe = document.createElement('iframe');
+ const whenIframeReady = new Promise((resolve, reject) => {
+ iframe.onload = resolve;
+ iframe.onerror = reject;
+ });
+
+ {%- for subtest in subtests %}
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [% subtest.origins %]);
+
+ declarations.push(`background-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_equals(headers['[%subtest.headerName%]'], '[%subtest.expected%]');
+ {%- endif %}
+ })
+ .then(t.step_func_done(), (error) => t.unreached_func());
+ }, 'background-image [%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [% subtest.origins %]);
+
+ declarations.push(`border-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'border-image [%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [% subtest.origins %]);
+
+ declarations.push(`content: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'content [%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [% subtest.origins %]);
+
+ declarations.push(`cursor: url("${url}"), auto;`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'cursor [%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ async_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [% subtest.origins %]);
+
+ declarations.push(`list-style-image: url("${url}");`);
+
+ whenIframeReady
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ })
+ .then(t.step_func_done(), t.unreached_func());
+ }, 'list-style-image [%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ {%- endfor %}
+
+ iframe.srcdoc = declarations.map((declaration, index) => `
+ <style>.el${index} { ${declaration} }</style><div class="el${index}"></div>`
+ ).join('');
+ document.body.appendChild(iframe);
+
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-a.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-a.sub.html
new file mode 100644
index 0000000..2bd8e8a
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-a.sub.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ {%- if subtests|length > 10 %}
+ <meta name="timeout" content="long">
+ {%- endif %}
+ <title>HTTP headers on request for HTML "a" element navigation</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ {%- if subtests|selectattr('userActivated')|list %}
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ {%- endif %}
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, {test, userActivated, attributes}) {
+ const win = window.open();
+ const anchor = win.document.createElement('a');
+ anchor.setAttribute('href', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ anchor.setAttribute(name, value);
+ }
+
+ win.document.body.appendChild(anchor);
+
+ test.add_cleanup(() => win.close());
+
+ if (userActivated) {
+ test_driver.bless('enable user activation', () => anchor.click());
+ } else {
+ anchor.click();
+ }
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, [% subtest.origins %], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: [%subtest.userActivated | default(false) | tojson%],
+ attributes: [%subtest.elementAttrs | default({}) | tojson%]
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%] - [%subtest.elementAttrs | collection("attributes")%][% " with user activation" if subtest.userActivated%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-area.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-area.sub.html
new file mode 100644
index 0000000..0cef5b2
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-area.sub.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ {%- if subtests|length > 10 %}
+ <meta name="timeout" content="long">
+ {%- endif %}
+ <title>HTTP headers on request for HTML "area" element navigation</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ {%- if subtests|selectattr('userActivated')|list %}
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ {%- endif %}
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, {test, userActivated, attributes}) {
+ const win = window.open();
+ const area = win.document.createElement('area');
+ area.setAttribute('href', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ area.setAttribute(name, value);
+ }
+
+ win.document.body.appendChild(area);
+
+ test.add_cleanup(() => win.close());
+
+ if (userActivated) {
+ test_driver.bless('enable user activation', () => area.click());
+ } else {
+ area.click();
+ }
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ makeRequestURL(key, [% subtest.origins %], {mime: 'text/html'}),
+ {
+ test: t,
+ userActivated: [%subtest.userActivated | default(false) | tojson%],
+ attributes: [%subtest.elementAttrs | default({}) | tojson%]
+ }
+ );
+
+ // `induceRequest` does not necessarily trigger a navigation, so the Python
+ // handler must be polled until it has received the initial request.
+ return retrieve(key, {poll: true})
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%] - [%subtest.elementAttrs | collection("attributes")%][% " with user activation" if subtest.userActivated%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-audio.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-audio.sub.html
new file mode 100644
index 0000000..92bc221
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-audio.sub.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "audio" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, attributes) {
+ const audio = document.createElement('audio');
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ audio.setAttribute(name, value);
+ }
+
+ return new Promise((resolve) => {
+ audio.setAttribute('src', url);
+ audio.onload = audio.onerror = resolve;
+ });
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [% subtest.origins %]),
+ [%subtest.elementAttrs | default({}) | tojson%]
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", ", ")%][%subtest.elementAttrs | collection("attributes")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-embed.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-embed.sub.html
new file mode 100644
index 0000000..18ce09e
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-embed.sub.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "embed" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ function induceRequest(t, url) {
+ const embed = document.createElement('embed');
+ embed.setAttribute('src', url);
+ document.body.appendChild(embed);
+
+ t.add_cleanup(() => embed.remove());
+
+ return new Promise((resolve) => embed.addEventListener('load', resolve));
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, [% subtest.origins %], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-frame.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-frame.sub.html
new file mode 100644
index 0000000..ce90171
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-frame.sub.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "frame" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ {%- if subtests|selectattr('userActivated')|list %}
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ {%- endif %}
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test, userActivated) {
+ const frame = document.createElement('frame');
+
+ const setSrc = () => frame.setAttribute('src', url);
+
+ document.body.appendChild(frame);
+ test.add_cleanup(() => frame.remove());
+
+ return new Promise((resolve) => {
+ if (userActivated) {
+ test_driver.bless('enable user activation', setSrc);
+ } else {
+ setSrc();
+ }
+
+ frame.onload = frame.onerror = resolve;
+ });
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [% subtest.origins %], {mime: 'text/html'}),
+ t,
+ [%subtest.userActivated | default(false) | tojson%]
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%][% " with user activation" if subtest.userActivated%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-iframe.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-iframe.sub.html
new file mode 100644
index 0000000..43a632a
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-iframe.sub.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "frame" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ {%- if subtests|selectattr('userActivated')|list %}
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ {%- endif %}
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test, userActivated) {
+ const iframe = document.createElement('iframe');
+
+ const setSrc = () => iframe.setAttribute('src', url);
+
+ document.body.appendChild(iframe);
+ test.add_cleanup(() => iframe.remove());
+
+ return new Promise((resolve) => {
+ if (userActivated) {
+ test_driver.bless('enable user activation', setSrc);
+ } else {
+ setSrc();
+ }
+
+ iframe.onload = iframe.onerror = resolve;
+ });
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [% subtest.origins %], {mime: 'text/html'}),
+ t,
+ [%subtest.userActivated | default(false) | tojson%]
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%][% " with user activation" if subtest.userActivated%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-img-environment-change.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-img-environment-change.sub.html
new file mode 100644
index 0000000..5a65114
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-img-environment-change.sub.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on image request triggered by change to environment</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ // The response to the request under test must describe a valid image
+ // resource in order for the `load` event to be fired.
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ function induceRequest(t, url, attributes) {
+ const iframe = document.createElement('iframe');
+ iframe.style.width = '50px';
+ document.body.appendChild(iframe);
+ t.add_cleanup(() => iframe.remove());
+ iframe.contentDocument.open();
+ iframe.contentDocument.close();
+
+ const image = iframe.contentDocument.createElement('img');
+ for (const [ name, value ] of Object.entries(attributes)) {
+ image.setAttribute(name, value);
+ }
+ iframe.contentDocument.body.appendChild(image);
+
+ image.setAttribute('srcset', `${url} 100w, /media/1x1-green.png 1w`);
+ image.setAttribute('sizes', '(max-width: 100px) 1px, (min-width: 150px) 123px');
+
+ return new Promise((resolve) => {
+ image.onload = image.onerror = resolve;
+ })
+ .then(() => {
+
+ iframe.style.width = '200px';
+
+ return new Promise((resolve) => image.onload = resolve);
+ });
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [% subtest.origins %], params),
+ [%subtest.elementAttrs | default({}) | tojson%]
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", ", ")%][%subtest.elementAttrs | collection("attributes")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-img.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-img.sub.html
new file mode 100644
index 0000000..1dac584
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-img.sub.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "img" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, sourceAttr, attributes) {
+ const image = document.createElement('img');
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ image.setAttribute(name, value);
+ }
+
+ return new Promise((resolve) => {
+ image.setAttribute(sourceAttr, url);
+ image.onload = image.onerror = resolve;
+ });
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [% subtest.origins %]),
+ '[%subtest.sourceAttr%]',
+ [%subtest.elementAttrs | default({}) | tojson%]
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.sourceAttr%] - [%subtest.description | pad("end", ", ")%][%subtest.elementAttrs | collection("attributes")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-input-image.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-input-image.sub.html
new file mode 100644
index 0000000..3c50008
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-input-image.sub.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "input" element with type="button"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test) {
+ const input = document.createElement('input');
+ input.setAttribute('type', 'image');
+
+ document.body.appendChild(input);
+ test.add_cleanup(() => input.remove());
+
+ return new Promise((resolve) => {
+ input.onload = input.onerror = resolve;
+ input.setAttribute('src', url);
+ });
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(makeRequestURL(key, [% subtest.origins %]), t)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", ", ")%][%subtest.elementAttrs | collection("attributes")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-link-icon.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-link-icon.sub.html
new file mode 100644
index 0000000..18ce12a
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-link-icon.sub.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ {%- if subtests|length > 10 %}
+ <meta name="timeout" content="long">
+ {%- endif %}
+ <title>HTTP headers on request for HTML "link" element with rel="icon"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ /**
+ * The `link` element supports a `load` event. That event would reliably
+ * indicate that the browser had received the request. Multiple major
+ * browsers do not implement the event, however, so in order to promote the
+ * visibility of this test, a less efficient polling-based detection
+ * mechanism is used.
+ *
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1638188
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=1083034
+ */
+ function induceRequest(t, url, attributes) {
+ const link = document.createElement('link');
+ link.setAttribute('rel', 'icon');
+ link.setAttribute('href', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ link.setAttribute(name, value);
+ }
+
+ document.head.appendChild(link);
+ t.add_cleanup(() => link.remove());
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, [% subtest.origins %], params),
+ [%subtest.elementAttrs | default({}) | tojson%]
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%] [%subtest.elementAttrs | collection("attributes")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-link-prefetch.optional.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-link-prefetch.optional.sub.html
new file mode 100644
index 0000000..59d677d
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-link-prefetch.optional.sub.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ {%- if subtests|length > 10 %}
+ <meta name="timeout" content="long">
+ {%- endif %}
+ <title>HTTP headers on request for HTML "link" element with rel="prefetch"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ /**
+ * The `link` element supports a `load` event. That event would reliably
+ * indicate that the browser had received the request. Multiple major
+ * browsers do not implement the event, however, so in order to promote the
+ * visibility of this test, a less efficient polling-based detection
+ * mechanism is used.
+ *
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1638188
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=1083034
+ */
+ function induceRequest(t, url, attributes) {
+ const link = document.createElement('link');
+ link.setAttribute('rel', 'prefetch');
+ link.setAttribute('href', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ link.setAttribute(name, value);
+ }
+
+ document.head.appendChild(link);
+ t.add_cleanup(() => link.remove());
+ }
+
+ setup(() => {
+ assert_implements_optional(document.createElement('link').relList.supports('prefetch'));
+ });
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ induceRequest(
+ t,
+ makeRequestURL(key, [% subtest.origins %]),
+ [%subtest.elementAttrs | default({}) | tojson%]
+ );
+
+ return retrieve(key, {poll:true})
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%] [%subtest.elementAttrs | collection("attributes")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
+
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-meta-refresh.optional.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-meta-refresh.optional.sub.html
new file mode 100644
index 0000000..5a8d8f8
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-meta-refresh.optional.sub.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "meta" element with http-equiv="refresh"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test) {
+ const win = window.open();
+ test.add_cleanup(() => win.close());
+
+ win.document.open();
+ win.document.write(
+ `<meta http-equiv="Refresh" content="0; URL=${url}">`
+ );
+ win.document.close();
+
+ return new Promise((resolve) => {
+ addEventListener('message', (event) => {
+ if (event.source === win) {
+ resolve();
+ }
+ });
+ });
+ }
+
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>opener.postMessage(0, '*')</${''}script>`
+ };
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [% subtest.origins %], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-picture.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-picture.sub.html
new file mode 100644
index 0000000..903aeed
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-picture.sub.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "picture" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, sourceEl, sourceAttr, attributes) {
+ const picture = document.createElement('picture');
+ const els = {
+ img: document.createElement('img'),
+ source: document.createElement('source')
+ };
+ picture.appendChild(els.source);
+ picture.appendChild(els.img);
+ document.body.appendChild(picture);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ els.img.setAttribute(name, value);
+ }
+
+ return new Promise((resolve) => {
+ els[sourceEl].setAttribute(sourceAttr, url);
+ els.img.onload = els.img.onerror = resolve;
+ });
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [% subtest.origins %]),
+ 'img',
+ 'src',
+ [%subtest.elementAttrs | default({}) | tojson%]
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - img[src] - [%subtest.description | pad("end", ", ")%][%subtest.elementAttrs | collection("attributes")%]');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [% subtest.origins %]),
+ 'img',
+ 'srcset',
+ [%subtest.elementAttrs | default({}) | tojson%]
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - img[srcset] - [%subtest.description | pad("end", ", ")%][%subtest.elementAttrs | collection("attributes")%]');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [% subtest.origins %]),
+ 'source',
+ 'srcset',
+ [%subtest.elementAttrs | default({}) | tojson%]
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - source[srcset] - [%subtest.description | pad("end", ", ")%][%subtest.elementAttrs | collection("attributes")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
+
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-script.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-script.sub.html
new file mode 100644
index 0000000..4a281ae
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-script.sub.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "script" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, attributes) {
+ const script = document.createElement('script');
+ script.setAttribute('src', url);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ script.setAttribute(name, value);
+ }
+
+ return new Promise((resolve, reject) => {
+ script.onload = resolve;
+ script.onerror = () => reject('Failed to load script');
+ document.body.appendChild(script);
+ })
+ .then(() => script.remove());
+ }
+ {%- for subtest in subtests %}
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [% subtest.origins %], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url,
+ [%subtest.elementAttrs | default({}) | tojson%]
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", ", ")%][%-subtest.elementAttrs | collection("attributes")%]');
+
+ {%- endfor %}
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-video-poster.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-video-poster.sub.html
new file mode 100644
index 0000000..9cdaf06
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-video-poster.sub.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "video" element "poster"</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ function induceRequest(t, url) {
+ var video = document.createElement('video');
+ video.setAttribute('poster', url);
+ document.body.appendChild(video);
+
+ const poll = () => {
+ if (video.clientWidth === 123) {
+ return;
+ }
+
+ return new Promise((resolve) => t.step_timeout(resolve, 0))
+ .then(poll);
+ };
+ t.add_cleanup(() => video.remove());
+
+ return poll();
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(t, makeRequestURL(key, [% subtest.origins %], params))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/element-video.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/element-video.sub.html
new file mode 100644
index 0000000..1b7b976
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/element-video.sub.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTML "video" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, attributes) {
+ const video = document.createElement('video');
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ video.setAttribute(name, value);
+ }
+
+ return new Promise((resolve) => {
+ video.setAttribute('src', url);
+ video.onload = video.onerror = resolve;
+ });
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [% subtest.origins %]),
+ [%subtest.elementAttrs | default({}) | tojson%]
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", ", ")%][%subtest.elementAttrs | collection("attributes")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/fetch-via-serviceworker.https.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/fetch-via-serviceworker.https.sub.html
new file mode 100644
index 0000000..eead710
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/fetch-via-serviceworker.https.sub.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ {%- if subtests|length > 10 %}
+ <meta name="timeout" content="long">
+ {%- endif %}
+ <title>HTTP headers on request using the "fetch" API and passing through a Serive Worker</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const scripts = {
+ fallback: '/fetch/metadata/resources/fetch-via-serviceworker--fallback--sw.js',
+ respondWith: '/fetch/metadata/resources/fetch-via-serviceworker--respondWith--sw.js'
+ };
+
+ function induceRequest(t, url, init, script) {
+ const SCOPE = '/fetch/metadata/resources/fetch-via-serviceworker-frame.html';
+ const SCRIPT = scripts[script];
+
+ return service_worker_unregister_and_register(t, SCRIPT, SCOPE)
+ .then((registration) => {
+ t.add_cleanup(() => registration.unregister());
+
+ return wait_for_state(t, registration.installing, 'activated');
+ })
+ .then(() => with_iframe(SCOPE))
+ .then((frame) => {
+ t.add_cleanup(() => frame.remove());
+
+ return frame.contentWindow.fetch(url, init);
+ });
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [% subtest.origins %]),
+ [%subtest.init | default({}) | tojson%],
+ 'respondWith'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", ", ")%][%subtest.init | collection("init")%] - respondWith');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [% subtest.origins %]),
+ [%subtest.init | default({}) | tojson%],
+ 'fallback'
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", ", ")%][%subtest.init | collection("init")%] - fallback');
+
+ {%- endfor %}
+
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/fetch.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/fetch.sub.html
new file mode 100644
index 0000000..a8dc536
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/fetch.sub.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request using the "fetch" API</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, init) {
+ return fetch(url, init);
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [% subtest.origins %]),
+ [%subtest.init | default({}) | tojson%]
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", ", ")%][%subtest.init | collection("init")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/form-submission.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/form-submission.sub.html
new file mode 100644
index 0000000..4c9c8c5
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/form-submission.sub.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <meta name="timeout" content="long">
+ <title>HTTP headers on request for HTML form navigation</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ {%- if subtests|selectattr('userActivated')|list %}
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ {%- endif %}
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(method, url, userActivated) {
+ const windowName = String(Math.random());
+ const form = document.createElement('form');
+ const submit = document.createElement('input');
+ submit.setAttribute('type', 'submit');
+ form.appendChild(submit);
+ const win = open('about:blank', windowName);
+ form.setAttribute('method', method);
+ form.setAttribute('action', url);
+ form.setAttribute('target', windowName);
+ document.body.appendChild(form);
+
+ // Query parameters must be expressed as form values so that they are sent
+ // with the submission of forms whose method is POST.
+ Array.from(new URL(url, location.origin).searchParams)
+ .forEach(([name, value]) => {
+ const input = document.createElement('input');
+ input.setAttribute('type', 'hidden');
+ input.setAttribute('name', name);
+ input.setAttribute('value', value);
+ form.appendChild(input);
+ });
+
+ return new Promise((resolve) => {
+ addEventListener('message', function(event) {
+ if (event.source === win) {
+ resolve();
+ }
+ });
+
+ if (userActivated) {
+ test_driver.click(submit);
+ } else {
+ submit.click();
+ }
+ })
+ .then(() => {
+ form.remove();
+ win.close();
+ });
+ }
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>opener.postMessage('done', '*')</${''}script>`
+ };
+
+ {%- for subtest in subtests %}
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [% subtest.origins %], responseParams);
+ const userActivated = [% 'true' if subtest.userActivated else 'false' %];
+ return induceRequest('[%subtest.method | default("POST")%]', url, userActivated)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", " - ")%][%subtest.method | default("POST")%][%" with user activation" if subtest.userActivated%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/header-link.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/header-link.sub.html
new file mode 100644
index 0000000..2831f22
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/header-link.sub.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for HTTP "Link" header</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, rel, test) {
+ const iframe = document.createElement('iframe');
+
+ iframe.setAttribute(
+ 'src',
+ '/fetch/metadata/resources/header-link.py' +
+ `?location=${encodeURIComponent(url)}&rel=${rel}`
+ );
+
+ document.body.appendChild(iframe);
+ test.add_cleanup(() => iframe.remove());
+
+ return new Promise((resolve) => {
+ iframe.onload = iframe.onerror = resolve;
+ });
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [% subtest.origins %], {mime: 'text/html'}),
+ '[%subtest.rel%]',
+ t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] rel=[%subtest.rel%][%subtest.description | pad("start", " - ")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/header-refresh.optional.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/header-refresh.optional.sub.html
new file mode 100644
index 0000000..ec963d5
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/header-refresh.optional.sub.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ {%- if subtests|length > 10 %}
+ <meta name="timeout" content="long">
+ {%- endif %}
+ <title>HTTP headers on request for HTTP "Refresh" header</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, test) {
+ const win = window.open();
+ test.add_cleanup(() => win.close());
+
+ win.location = `/common/refresh.py?location=${encodeURIComponent(url)}`
+
+ return new Promise((resolve) => {
+ addEventListener('message', (event) => {
+ if (event.source === win) {
+ resolve();
+ }
+ });
+ });
+ }
+
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>opener.postMessage(0, '*')</${''}script>`
+ };
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(key, [% subtest.origins %], responseParams), t
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/script-module-import-dynamic.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/script-module-import-dynamic.sub.html
new file mode 100644
index 0000000..653d3cd
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/script-module-import-dynamic.sub.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for dynamic ECMAScript module import</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <script type="module">
+ 'use strict';
+ {%- for subtest in subtests %}
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [% subtest.origins %], { mime: 'application/javascript' }
+ );
+
+ return import(url)
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ {%- endfor %}
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/script-module-import-static.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/script-module-import-static.sub.html
new file mode 100644
index 0000000..c8d5f95
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/script-module-import-static.sub.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for static ECMAScript module import</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url) {
+ const script = document.createElement('script');
+ script.setAttribute('type', 'module');
+ script.setAttribute(
+ 'src',
+ '/fetch/metadata/resources/es-module.sub.js?moduleId=' + encodeURIComponent(url)
+ );
+
+ return new Promise((resolve, reject) => {
+ script.onload = resolve;
+ script.onerror = () => reject('Failed to load script');
+ document.body.appendChild(script);
+ })
+ .then(() => script.remove());
+ }
+ {%- for subtest in subtests %}
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ makeRequestURL(
+ key, [% subtest.origins %], { mime: 'application/javascript' }
+ )
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ {%- endfor %}
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/serviceworker.https.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/serviceworker.https.sub.html
new file mode 100644
index 0000000..8284325
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/serviceworker.https.sub.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<!DOCTYPE html>
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for Service Workers</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(t, url, options, event, clear) {
+ // Register a service worker and check the request header.
+ return navigator.serviceWorker.register(url, options)
+ .then((registration) => {
+ t.add_cleanup(() => registration.unregister());
+ if (event === 'register') {
+ return;
+ }
+ return clear().then(() => registration.update());
+ });
+ }
+
+ {%- for subtest in subtests %}
+ {%- set origin = subtest.origins[0]|default('httpsOrigin') %}
+ {%- if origin == 'httpsOrigin' or not origin %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [% subtest.origins %], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(t, url, [%subtest.options | default({}) | tojson%], 'register')
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", ", ")%][%subtest.options | collection("options")%] - registration');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [% subtest.origins %], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(t, url, [%subtest.options | default({}) | tojson%], 'update', () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", ", ")%][%subtest.options | collection("options")%] - updating');
+
+ {%- endif %}
+ {%- endfor %}
+ </script>
+</body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/svg-image.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/svg-image.sub.html
new file mode 100644
index 0000000..52f7806
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/svg-image.sub.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ {%- if subtests|length > 10 %}
+ <meta name="timeout" content="long">
+ {%- endif %}
+ <title>HTTP headers on request for SVG "image" element source</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const params = {
+ body: `
+ <svg xmlns="http://www.w3.org/2000/svg" width="123" height="123">
+ <rect fill="lime" width="123" height="123"/>
+ </svg>
+ `,
+ mime: 'image/svg+xml'
+ };
+
+ function induceRequest(t, url, attributes) {
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ svg.setAttributeNS(
+ "http://www.w3.org/2000/xmlns/",
+ "xmlns:xlink",
+ "http://www.w3.org/1999/xlink"
+ );
+ const image = document.createElementNS("http://www.w3.org/2000/svg", "image");
+ image.setAttribute("href", url);
+ svg.appendChild(image);
+
+ for (const [ name, value ] of Object.entries(attributes)) {
+ image.setAttribute(name, value);
+ }
+
+ document.body.appendChild(svg);
+ t.add_cleanup(() => svg.remove());
+
+ return new Promise((resolve, reject) => {
+ image.onload = resolve;
+ image.onerror = reject;
+ });
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+
+ return induceRequest(
+ t,
+ makeRequestURL(key, [% subtest.origins %], params),
+ [%subtest.elementAttrs | default({}) | tojson%]
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%] [%subtest.elementAttrs | collection("attributes")%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/window-history.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/window-history.sub.html
new file mode 100644
index 0000000..286d019
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/window-history.sub.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ {%- if subtests|length > 10 %}
+ <meta name="timeout" content="long">
+ {%- endif %}
+ <title>HTTP headers on request for navigation via the HTML History API</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ {%- if subtests|selectattr('userActivated')|list %}
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ {%- endif %}
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ const whenDone = (win) => {
+ return new Promise((resolve) => {
+ addEventListener('message', function handle(event) {
+ if (event.source === win) {
+ resolve();
+ removeEventListener('message', handle);
+ }
+ });
+ })
+ };
+
+ /**
+ * Prime the UA's session history such that the location of the request is
+ * immediately behind the current entry. Because the location may not be
+ * same-origin with the current browsing context, this must be done via a
+ * true navigation and not, e.g. the `history.pushState` API. The initial
+ * navigation will alter the WPT server's internal state; in order to avoid
+ * false positives, clear that state prior to initiating the second
+ * navigation via `history.back`.
+ */
+ function induceBackRequest(url, test, clear) {
+ const win = window.open(url);
+
+ test.add_cleanup(() => win.close());
+
+ return whenDone(win)
+ .then(clear)
+ .then(() => win.history.back())
+ .then(() => whenDone(win));
+ }
+
+ /**
+ * Prime the UA's session history such that the location of the request is
+ * immediately ahead of the current entry. Because the location may not be
+ * same-origin with the current browsing context, this must be done via a
+ * true navigation and not, e.g. the `history.pushState` API. The initial
+ * navigation will alter the WPT server's internal state; in order to avoid
+ * false positives, clear that state prior to initiating the second
+ * navigation via `history.forward`.
+ */
+ function induceForwardRequest(url, test, clear) {
+ const win = window.open(messageOpenerUrl);
+
+ test.add_cleanup(() => win.close());
+
+ return whenDone(win)
+ .then(() => win.location = url)
+ .then(() => whenDone(win))
+ .then(clear)
+ .then(() => win.history.go(-2))
+ .then(() => whenDone(win))
+ .then(() => win.history.forward())
+ .then(() => whenDone(win));
+ }
+
+ const messageOpenerUrl = new URL(
+ '/fetch/metadata/resources/message-opener.html', location
+ );
+ // For these tests to function, replacement must *not* be enabled during
+ // navigation. Assignment must therefore take place after the document has
+ // completely loaded [1]. This event is not directly observable, but it is
+ // scheduled as a task immediately following the global object's `load`
+ // event [2]. By queuing a task during the dispatch of the `load` event,
+ // navigation can be consistently triggered without replacement.
+ //
+ // [1] https://html.spec.whatwg.org/multipage/history.html#location-object-setter-navigate
+ // [2] https://html.spec.whatwg.org/multipage/parsing.html#the-end
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>
+ window.addEventListener('load', () => {
+ set`+`Timeout(() => location.assign('${messageOpenerUrl}'));
+ });
+ <`+`/script>`
+ };
+ {%- for subtest in subtests %}
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [% subtest.origins %], responseParams);
+
+ return induceBackRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("right", " - ")%]history.back[%subtest.api%][% " with user activation" if subtest.userActivated%]');
+
+ promise_test((t) => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [% subtest.origins %], responseParams);
+
+ return induceForwardRequest(url, t, () => retrieve(key))
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("right", " - ")%]history.forward[%subtest.api%][% " with user activation" if subtest.userActivated%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/window-location.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/window-location.sub.html
new file mode 100644
index 0000000..96f3912
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/window-location.sub.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ {%- if subtests|length > 10 %}
+ <meta name="timeout" content="long">
+ {%- endif %}
+ <title>HTTP headers on request for navigation via the HTML Location API</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ {%- if subtests|selectattr('userActivated')|list %}
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ {%- endif %}
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <body>
+ <script>
+ 'use strict';
+
+ function induceRequest(url, navigate, userActivated) {
+ const win = window.open();
+
+ return new Promise((resolve) => {
+ addEventListener('message', function(event) {
+ if (event.source === win) {
+ resolve();
+ }
+ });
+
+ if (userActivated) {
+ test_driver.bless('enable user activation', () => {
+ navigate(win, url);
+ });
+ } else {
+ navigate(win, url);
+ }
+ })
+ .then(() => win.close());
+ }
+
+ const responseParams = {
+ mime: 'text/html',
+ body: `<script>opener.postMessage('done', '*')</${''}script>`
+ };
+ {%- for subtest in subtests %}
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [% subtest.origins %], responseParams);
+
+ const navigate = (win, path) => {
+ win.location = path;
+ };
+ return induceRequest(url, navigate, [% 'true' if subtest.userActivated else 'false' %])
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", " - ")%]location[% " with user activation" if subtest.userActivated%]');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [% subtest.origins %], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.href = path;
+ };
+ return induceRequest(url, navigate, [% 'true' if subtest.userActivated else 'false' %])
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", " - ")%]location.href[% " with user activation" if subtest.userActivated%]');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [% subtest.origins %], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.assign(path);
+ };
+ return induceRequest(url, navigate, [% 'true' if subtest.userActivated else 'false' %])
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", " - ")%]location.assign[% " with user activation" if subtest.userActivated%]');
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(key, [% subtest.origins %], responseParams);
+
+ const navigate = (win, path) => {
+ win.location.replace(path);
+ };
+ return induceRequest(url, navigate, [% 'true' if subtest.userActivated else 'false' %])
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", " - ")%]location.replace[% " with user activation" if subtest.userActivated%]');
+
+ {%- endfor %}
+ </script>
+ </body>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/worker-dedicated-constructor.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/worker-dedicated-constructor.sub.html
new file mode 100644
index 0000000..fede596
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/worker-dedicated-constructor.sub.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for dedicated worker via the "Worker" constructor</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <script type="module">
+ 'use strict';
+ function induceRequest(url, options) {
+ return new Promise((resolve, reject) => {
+ const worker = new Worker(url, options);
+ worker.onmessage = resolve;
+ worker.onerror = reject;
+ });
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key,
+ [% subtest.origins %],
+ { mime: 'application/javascript', body: 'postMessage("")' }
+ );
+
+ return induceRequest(url
+ {%- if subtest.options -%}
+ , [% subtest.options | tojson %]
+ {%- endif -%}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%] - [%subtest.description | pad("end", ", ")%][%subtest.options|collection("options")%]');
+
+ {%- endfor %}
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/tools/templates/worker-dedicated-importscripts.sub.html b/test/wpt/tests/fetch/metadata/tools/templates/worker-dedicated-importscripts.sub.html
new file mode 100644
index 0000000..93e6374
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/tools/templates/worker-dedicated-importscripts.sub.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<!--
+[%provenance%]
+-->
+<html lang="en">
+ <meta charset="utf-8">
+ <title>HTTP headers on request for dedicated worker via the "importScripts" API</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/fetch/metadata/resources/helper.sub.js"></script>
+ <script type="module">
+ 'use strict';
+ function induceRequest(url, options) {
+ const src = `
+ importScripts('${url}');
+ postMessage('done');
+ `;
+ const workerUrl = URL.createObjectURL(
+ new Blob([src], { type: 'application/javascript' })
+ );
+ return new Promise((resolve, reject) => {
+ const worker = new Worker(workerUrl, options);
+ worker.onmessage = resolve;
+ worker.onerror = reject;
+ });
+ }
+
+ {%- for subtest in subtests %}
+
+ promise_test(() => {
+ const key = '{{uuid()}}';
+ const url = makeRequestURL(
+ key, [% subtest.origins %], { mime: 'application/javascript' }
+ );
+
+ return induceRequest(url
+ {%- if subtest.options -%}
+ , [% subtest.options | tojson %]
+ {%- endif -%}
+ )
+ .then(() => retrieve(key))
+ .then((headers) => {
+ {%- if subtest.expected == none %}
+ assert_not_own_property(headers, '[%subtest.headerName%]');
+ {%- else %}
+ assert_own_property(headers, '[%subtest.headerName%]');
+ assert_array_equals(headers['[%subtest.headerName%]'], ['[%subtest.expected%]']);
+ {%- endif %}
+ });
+ }, '[%subtest.headerName%][%subtest.description | pad("start", " - ")%]');
+
+ {%- endfor %}
+ </script>
+</html>
diff --git a/test/wpt/tests/fetch/metadata/track.https.sub.html b/test/wpt/tests/fetch/metadata/track.https.sub.html
new file mode 100644
index 0000000..346798f
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/track.https.sub.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+
+<link rel="author" href="mtrzos@google.com" title="Maciek Trzos">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<body>
+</body>
+<script>
+ let nonce = token();
+
+ function createVideoElement() {
+ let el = document.createElement('video');
+ el.src = "/media/movie_5.mp4";
+ el.setAttribute("controls", "");
+ el.setAttribute("crossorigin", "");
+ return el;
+ }
+
+ function createTrack() {
+ let el = document.createElement("track");
+ el.setAttribute("default", "");
+ el.setAttribute("kind", "captions");
+ el.setAttribute("srclang", "en");
+ return el;
+ }
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = "track-same-origin" + nonce;
+ let video = createVideoElement();
+ let el = createTrack();
+ el.src = "https://{{host}}:{{ports[https][0]}}/fetch/metadata/resources/record-header.py?file=" + key;
+ el.onload = t.step_func(_ => {
+ expected = {
+ "site": "same-origin",
+ "user": "",
+ "mode": "cors", // Because the `video` element has `crossorigin`
+ "dest": "track"
+ };
+ validate_expectations(key, expected, "Same-Origin track")
+ .then(_ => resolve());
+ });
+ video.appendChild(el);
+ document.body.appendChild(video);
+ });
+ }, "Same-Origin track");
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = "track-same-site" + nonce;
+ let video = createVideoElement();
+ let el = createTrack();
+ el.src = "https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/metadata/resources/record-header.py?file=" + key;
+ el.onload = t.step_func(_ => {
+ expected = {
+ "site": "same-site",
+ "user": "",
+ "mode": "cors", // Because the `video` element has `crossorigin`
+ "dest": "track"
+ };
+ validate_expectations(key, expected, "Same-Site track")
+ .then(resolve)
+ .catch(reject);
+
+ });
+ video.appendChild(el);
+ document.body.appendChild(video);
+ });
+ }, "Same-Site track");
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = "track-cross-site" + nonce;
+ let video = createVideoElement();
+ let el = createTrack();
+ el.src = "https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/metadata/resources/record-header.py?file=" + key;
+ el.onload = t.step_func(_ => {
+ expected = {
+ "site": "cross-site",
+ "user": "",
+ "mode": "cors", // Because the `video` element has `crossorigin`
+ "dest": "track"
+ };
+ validate_expectations(key, expected,"Cross-Site track")
+ .then(resolve)
+ .catch(reject);
+ });
+ video.appendChild(el);
+ document.body.appendChild(video);
+ });
+ }, "Cross-Site track");
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = "track-same-origin-cors" + nonce;
+ let video = createVideoElement();
+
+ // Unset `crossorigin` to change the CORS mode:
+ video.crossOrigin = undefined;
+
+ let el = createTrack();
+ el.src = "https://{{host}}:{{ports[https][0]}}/fetch/metadata/resources/record-header.py?file=" + key;
+ el.onload = t.step_func(_ => {
+ expected = {
+ "site":"same-origin",
+ "user":"",
+ "mode": "same-origin",
+ "dest": "track"
+ };
+ validate_expectations(key, expected, "Same-Origin, CORS track")
+ .then(_ => resolve());
+ });
+ video.appendChild(el);
+ document.body.appendChild(video);
+ });
+ }, "Same-Origin, CORS track");
+</script>
diff --git a/test/wpt/tests/fetch/metadata/trailing-dot.https.sub.any.js b/test/wpt/tests/fetch/metadata/trailing-dot.https.sub.any.js
new file mode 100644
index 0000000..5e32fc4
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/trailing-dot.https.sub.any.js
@@ -0,0 +1,30 @@
+// META: global=window,worker
+// META: script=/fetch/metadata/resources/helper.js
+
+// Site
+promise_test(t => {
+ return validate_expectations_custom_url("https://{{host}}.:{{ports[https][0]}}/fetch/metadata/resources/echo-as-json.py", {}, {
+ "site": "cross-site",
+ "user": "",
+ "mode": "cors",
+ "dest": "empty"
+ }, "Fetching a resource from the same origin, but spelled with a trailing dot.");
+}, "Fetching a resource from the same origin, but spelled with a trailing dot.");
+
+promise_test(t => {
+ return validate_expectations_custom_url("https://{{hosts[][www]}}.:{{ports[https][0]}}/fetch/metadata/resources/echo-as-json.py", {}, {
+ "site": "cross-site",
+ "user": "",
+ "mode": "cors",
+ "dest": "empty"
+ }, "Fetching a resource from the same site, but spelled with a trailing dot.");
+}, "Fetching a resource from the same site, but spelled with a trailing dot.");
+
+promise_test(t => {
+ return validate_expectations_custom_url("https://{{hosts[alt][www]}}.:{{ports[https][0]}}/fetch/metadata/resources/echo-as-json.py", {}, {
+ "site": "cross-site",
+ "user": "",
+ "mode": "cors",
+ "dest": "empty"
+ }, "Fetching a resource from a cross-site host, spelled with a trailing dot.");
+}, "Fetching a resource from a cross-site host, spelled with a trailing dot.");
diff --git a/test/wpt/tests/fetch/metadata/unload.https.sub.html b/test/wpt/tests/fetch/metadata/unload.https.sub.html
new file mode 100644
index 0000000..bc26048
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/unload.https.sub.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<body>
+<script>
+ // The test
+ // 1. Creates a same-origin iframe
+ // 2. Adds to the iframe an unload handler that will
+ // trigger a request to <unload_request_url>/.../record-header.py...
+ // 3. Navigate the iframe to a cross-origin url (to data: url)
+ // 4. Waits until the request goes through
+ // 5. Verifies Sec-Fetch-Site request header of the request.
+ //
+ // This is a regression test for https://crbug.com/986577.
+ function create_test(unload_request_origin, expectations) {
+ async_test(t => {
+ // STEP 1: Create an iframe.
+ let nonce = token();
+ let key = "unload-test-" + nonce;
+ let url = unload_request_origin +
+ "/fetch/metadata/resources/record-header.py?file=" + key;
+ let i = document.createElement('iframe');
+ i.src = 'resources/unload-with-beacon.html';
+ i.onload = () => {
+ // STEP 2: Ask the iframe to add an unload handler.
+ i.contentWindow.postMessage(url, '*');
+ };
+ window.addEventListener('message', e => {
+ // STEP 3: Navigate the iframe away
+ i.contentWindow.location = 'data:text/html,DONE';
+ });
+ document.body.appendChild(i);
+
+ // STEPS 4 and 5: Wait for the beacon to go through and verify
+ // the request headers.
+ function wait_and_verify() {
+ t.step_timeout(() => {
+ fetch("resources/record-header.py?retrieve=true&file=" + key)
+ .then(response => response.text())
+ .then(text => t.step(() => {
+ if (text == 'No header has been recorded') {
+ wait_and_verify();
+ return;
+ }
+ assert_header_equals(text, expectations);
+ t.done();
+ }))
+ }, 200);
+ }
+ wait_and_verify();
+ }, "Fetch from an unload handler");
+ }
+
+ create_test("https://{{host}}:{{ports[https][0]}}", {
+ "site": "same-origin",
+ "user": "",
+ "mode": "no-cors",
+ "dest": "empty"
+ });
+</script>
diff --git a/test/wpt/tests/fetch/metadata/window-open.https.sub.html b/test/wpt/tests/fetch/metadata/window-open.https.sub.html
new file mode 100644
index 0000000..94ba76a
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/window-open.https.sub.html
@@ -0,0 +1,199 @@
+<!DOCTYPE html>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<body>
+<script>
+ // Forced navigations:
+ async_test(t => {
+ let w = window.open("https://{{host}}:{{ports[https][0]}}/fetch/metadata/resources/post-to-owner.py");
+ t.add_cleanup(_ => w.close());
+ window.addEventListener('message', t.step_func(e => {
+ if (e.source != w)
+ return;
+
+ assert_header_equals(e.data, {
+ "site": "same-origin",
+ "user": "",
+ "mode": "navigate",
+ "dest": "document"
+ }, "Same-origin window, forced");
+ t.done();
+ }));
+ }, "Same-origin window, forced");
+
+ async_test(t => {
+ let w = window.open("https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/metadata/resources/post-to-owner.py");
+ t.add_cleanup(_ => w.close());
+ window.addEventListener('message', t.step_func(e => {
+ if (e.source != w)
+ return;
+
+ assert_header_equals(e.data, {
+ "site": "same-site",
+ "user": "",
+ "mode": "navigate",
+ "dest": "document"
+ }, "Same-site window, forced");
+ t.done();
+ }));
+ }, "Same-site window, forced");
+
+ async_test(t => {
+ let w = window.open("https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/metadata/resources/post-to-owner.py");
+ t.add_cleanup(_ => w.close());
+ window.addEventListener('message', t.step_func(e => {
+ if (e.source != w)
+ return;
+
+ assert_header_equals(e.data, {
+ "site": "cross-site",
+ "user": "",
+ "mode": "navigate",
+ "dest": "document"
+ }, "Cross-site window, forced");
+ t.done();
+ }));
+ }, "Cross-site window, forced");
+
+ async_test(t => {
+ let w = window.open("https://{{host}}:{{ports[https][0]}}/fetch/metadata/resources/post-to-owner.py");
+ t.add_cleanup(_ => w.close());
+ let messages = 0;
+ window.addEventListener('message', t.step_func(e => {
+ messages++;
+ if (e.source != w)
+ return;
+
+ assert_header_equals(e.data, {
+ "site": "same-origin",
+ "user": "",
+ "mode": "navigate",
+ "dest": "document"
+ }, "Same-origin window, forced, reloaded");
+
+ if (messages == 1) {
+ w.location.reload();
+ } else {
+ t.done();
+ }
+ }));
+ }, "Same-origin window, forced, reloaded");
+
+ async_test(t => {
+ let w = window.open("https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/metadata/resources/post-to-owner.py");
+ t.add_cleanup(_ => w.close());
+ let messages = 0;
+ window.addEventListener('message', t.step_func(e => {
+ messages++;
+ if (e.source != w)
+ return;
+
+ assert_header_equals(e.data, {
+ "site": "same-site",
+ "user": "",
+ "mode": "navigate",
+ "dest": "document"
+ }, "Same-site window, forced, reloaded");
+
+ if (messages == 1) {
+ w.location.reload();
+ } else {
+ t.done();
+ }
+ }));
+ }, "Same-site window, forced, reloaded");
+
+ async_test(t => {
+ let w = window.open("https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/metadata/resources/post-to-owner.py");
+ t.add_cleanup(_ => w.close());
+ let messages = 0;
+ window.addEventListener('message', t.step_func(e => {
+ messages++;
+ if (e.source != w)
+ return;
+
+ assert_header_equals(e.data, {
+ "site": "cross-site",
+ "user": "",
+ "mode": "navigate",
+ "dest": "document"
+ }, "Cross-site window, forced, reloaded");
+
+ if (messages == 1) {
+ w.location.reload();
+ } else {
+ t.done();
+ }
+ }));
+ }, "Cross-site window, forced, reloaded");
+
+ // User-activated navigations:
+ async_test(t => {
+ let b = document.createElement('button');
+ b.onclick = t.step_func(_ => {
+ let w = window.open("https://{{host}}:{{ports[https][0]}}/fetch/metadata/resources/post-to-owner.py");
+ t.add_cleanup(_ => w.close());
+ window.addEventListener('message', t.step_func(e => {
+ if (e.source != w)
+ return;
+
+ assert_header_equals(e.data, {
+ "site": "same-origin",
+ "user": "?1",
+ "mode": "navigate",
+ "dest": "document"
+ }, "Same-origin window, user-activated");
+ t.done();
+ }));
+ });
+ document.body.appendChild(b);
+ test_driver.click(b);
+ }, "Same-origin window, user-activated");
+
+ async_test(t => {
+ let b = document.createElement('button');
+ b.onclick = t.step_func(_ => {
+ let w = window.open("https://{{hosts[][www]}}:{{ports[https][0]}}/fetch/metadata/resources/post-to-owner.py");
+ t.add_cleanup(_ => w.close());
+ window.addEventListener('message', t.step_func(e => {
+ if (e.source != w)
+ return;
+
+ assert_header_equals(e.data, {
+ "site": "same-site",
+ "user": "?1",
+ "mode": "navigate",
+ "dest": "document"
+ }, "Same-site window, user-activated");
+ t.done();
+ }));
+ });
+ document.body.appendChild(b);
+ test_driver.click(b);
+ }, "Same-site window, user-activated");
+
+ async_test(t => {
+ let b = document.createElement('button');
+ b.onclick = t.step_func(_ => {
+ let w = window.open("https://{{hosts[alt][www]}}:{{ports[https][0]}}/fetch/metadata/resources/post-to-owner.py");
+ t.add_cleanup(_ => w.close());
+ window.addEventListener('message', t.step_func(e => {
+ if (e.source != w)
+ return;
+
+ assert_header_equals(e.data, {
+ "site": "cross-site",
+ "user": "?1",
+ "mode": "navigate",
+ "dest": "document"
+ }, "Cross-site window, user-activated");
+ t.done();
+ }));
+ });
+ document.body.appendChild(b);
+ test_driver.click(b);
+ }, "Cross-site window, user-activated");
+</script>
diff --git a/test/wpt/tests/fetch/metadata/worker.https.sub.html b/test/wpt/tests/fetch/metadata/worker.https.sub.html
new file mode 100644
index 0000000..20a4fe5
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/worker.https.sub.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+
+<link rel="author" href="mtrzos@google.com" title="Maciek Trzos">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<script>
+ let nonce = token();
+
+ promise_test(t => {
+ return new Promise((resolve, reject) => {
+ let key = "worker-same-origin" + nonce;
+ let w = new Worker("/fetch/metadata/resources/record-header.py?file=" + key);
+ w.onmessage = e => {
+ let expected = {"site":"same-origin", "user":"", "mode": "same-origin", "dest": "worker"};
+ validate_expectations(key, expected)
+ .then(_ => resolve())
+ .catch(e => reject(e));
+ };
+ });
+ }, "Same-Origin worker");
+</script>
+<body></body>
diff --git a/test/wpt/tests/fetch/metadata/xslt.https.sub.html b/test/wpt/tests/fetch/metadata/xslt.https.sub.html
new file mode 100644
index 0000000..dc72d7b
--- /dev/null
+++ b/test/wpt/tests/fetch/metadata/xslt.https.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+
+<link rel="author" href="mtrzos@google.com" title="Maciek Trzos">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<script>
+ // Open a window with XML document which loads resources via <?xml-stylesheet/> tag
+ let nonce = token();
+ let w = window.open("resources/xslt-test.sub.xml?token=" + nonce);
+ window.addEventListener('message', function(e) {
+ if (e.source != w)
+ return;
+
+ // Only testing same-origin XSLT because same-site and cross-site XSLT is blocked.
+ promise_test(t => {
+ let expected = {"site":"same-origin", "user":"", "mode": "same-origin", "dest": "xslt"};
+ return validate_expectations("xslt-same-origin" + nonce, expected);
+ }, "Same-Origin xslt");
+
+ w.close();
+ });
+
+</script>