summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/fetch/metadata/resources
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/fetch/metadata/resources
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/fetch/metadata/resources')
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/appcache-iframe.sub.html15
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/dedicatedWorker.js1
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/echo-as-json.py29
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/echo-as-script.py14
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/es-module.sub.js1
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/fetch-via-serviceworker--fallback--sw.js3
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/fetch-via-serviceworker--respondWith--sw.js3
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/fetch-via-serviceworker-frame.html3
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/header-link.py15
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/helper.js42
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/helper.sub.js67
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/message-opener.html17
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/post-to-owner.py36
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/record-header.py145
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/record-headers.py73
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/redirectTestHelper.sub.js167
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/serviceworker-accessors-frame.html3
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/serviceworker-accessors.sw.js14
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/sharedWorker.js9
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/unload-with-beacon.html12
-rw-r--r--testing/web-platform/tests/fetch/metadata/resources/xslt-test.sub.xml12
21 files changed, 681 insertions, 0 deletions
diff --git a/testing/web-platform/tests/fetch/metadata/resources/appcache-iframe.sub.html b/testing/web-platform/tests/fetch/metadata/resources/appcache-iframe.sub.html
new file mode 100644
index 0000000000..cea9a4feae
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/dedicatedWorker.js b/testing/web-platform/tests/fetch/metadata/resources/dedicatedWorker.js
new file mode 100644
index 0000000000..18626d3d84
--- /dev/null
+++ b/testing/web-platform/tests/fetch/metadata/resources/dedicatedWorker.js
@@ -0,0 +1 @@
+self.postMessage("Loaded");
diff --git a/testing/web-platform/tests/fetch/metadata/resources/echo-as-json.py b/testing/web-platform/tests/fetch/metadata/resources/echo-as-json.py
new file mode 100644
index 0000000000..44f68e8fe9
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/echo-as-script.py b/testing/web-platform/tests/fetch/metadata/resources/echo-as-script.py
new file mode 100644
index 0000000000..1e7bc91184
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/es-module.sub.js b/testing/web-platform/tests/fetch/metadata/resources/es-module.sub.js
new file mode 100644
index 0000000000..f9668a3dc6
--- /dev/null
+++ b/testing/web-platform/tests/fetch/metadata/resources/es-module.sub.js
@@ -0,0 +1 @@
+import '{{GET[moduleId]}}';
diff --git a/testing/web-platform/tests/fetch/metadata/resources/fetch-via-serviceworker--fallback--sw.js b/testing/web-platform/tests/fetch/metadata/resources/fetch-via-serviceworker--fallback--sw.js
new file mode 100644
index 0000000000..09858b2663
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/fetch-via-serviceworker--respondWith--sw.js b/testing/web-platform/tests/fetch/metadata/resources/fetch-via-serviceworker--respondWith--sw.js
new file mode 100644
index 0000000000..8bf8d8f221
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/fetch-via-serviceworker-frame.html b/testing/web-platform/tests/fetch/metadata/resources/fetch-via-serviceworker-frame.html
new file mode 100644
index 0000000000..9879802500
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/header-link.py b/testing/web-platform/tests/fetch/metadata/resources/header-link.py
new file mode 100644
index 0000000000..de891163a3
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/helper.js b/testing/web-platform/tests/fetch/metadata/resources/helper.js
new file mode 100644
index 0000000000..725f9a7e43
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/helper.sub.js b/testing/web-platform/tests/fetch/metadata/resources/helper.sub.js
new file mode 100644
index 0000000000..fd179fe6f2
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/message-opener.html b/testing/web-platform/tests/fetch/metadata/resources/message-opener.html
new file mode 100644
index 0000000000..eb2af7b250
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/post-to-owner.py b/testing/web-platform/tests/fetch/metadata/resources/post-to-owner.py
new file mode 100644
index 0000000000..256dd6e49d
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/record-header.py b/testing/web-platform/tests/fetch/metadata/resources/record-header.py
new file mode 100644
index 0000000000..29ff2ed798
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/record-headers.py b/testing/web-platform/tests/fetch/metadata/resources/record-headers.py
new file mode 100644
index 0000000000..0362fe228c
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/redirectTestHelper.sub.js b/testing/web-platform/tests/fetch/metadata/resources/redirectTestHelper.sub.js
new file mode 100644
index 0000000000..1bfbbae70c
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/serviceworker-accessors-frame.html b/testing/web-platform/tests/fetch/metadata/resources/serviceworker-accessors-frame.html
new file mode 100644
index 0000000000..9879802500
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/serviceworker-accessors.sw.js b/testing/web-platform/tests/fetch/metadata/resources/serviceworker-accessors.sw.js
new file mode 100644
index 0000000000..36c55a7786
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/sharedWorker.js b/testing/web-platform/tests/fetch/metadata/resources/sharedWorker.js
new file mode 100644
index 0000000000..5eb89cb4f6
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/unload-with-beacon.html b/testing/web-platform/tests/fetch/metadata/resources/unload-with-beacon.html
new file mode 100644
index 0000000000..b00c9a5776
--- /dev/null
+++ b/testing/web-platform/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/testing/web-platform/tests/fetch/metadata/resources/xslt-test.sub.xml b/testing/web-platform/tests/fetch/metadata/resources/xslt-test.sub.xml
new file mode 100644
index 0000000000..acb478ab64
--- /dev/null
+++ b/testing/web-platform/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>