diff --git a/common/refresh.py b/common/refresh.py new file mode 100644 index 00000000000000..28a065d92c2721 --- /dev/null +++ b/common/refresh.py @@ -0,0 +1,11 @@ +def main(request, response): + """ + Respond with a blank HTML document and a `Refresh` header which describes + an immediate redirect to the URL specified by the requests `location` query + string parameter + """ + headers = [ + ('Content-Type', 'text/html'), + ('Refresh', '0; URL={}'.format(request.GET.first('location'))) + ] + return (200, headers, '') diff --git a/fetch/api/resources/redirect.py b/fetch/api/resources/redirect.py index eb5c98dd52ff8b..1f392eeeff0f19 100644 --- a/fetch/api/resources/redirect.py +++ b/fetch/api/resources/redirect.py @@ -33,6 +33,8 @@ def main(request, response): if "redirect_status" in request.GET: status = int(request.GET['redirect_status']) + elif "redirect_status" in request.POST: + status = int(request.POST['redirect_status']) stashed_data['count'] += 1 diff --git a/fetch/metadata/generated/appcache-manifest.https.sub.html b/fetch/metadata/generated/appcache-manifest.https.sub.html new file mode 100644 index 00000000000000..1250220cdd4515 --- /dev/null +++ b/fetch/metadata/generated/appcache-manifest.https.sub.html @@ -0,0 +1,296 @@ + + + + + HTTP headers on request for Appcache manifest + + + + + + + diff --git a/fetch/metadata/generated/audioworklet.https.sub.html b/fetch/metadata/generated/audioworklet.https.sub.html new file mode 100644 index 00000000000000..a821fe073ec775 --- /dev/null +++ b/fetch/metadata/generated/audioworklet.https.sub.html @@ -0,0 +1,214 @@ + + + + + HTTP headers on request for AudioWorklet module + + + + + + + + diff --git a/fetch/metadata/generated/cors-preflight.https.sub.html b/fetch/metadata/generated/cors-preflight.https.sub.html new file mode 100644 index 00000000000000..545516a05703f4 --- /dev/null +++ b/fetch/metadata/generated/cors-preflight.https.sub.html @@ -0,0 +1,71 @@ + + + + + HTTP headers on CORS preflight request + + + + + + + diff --git a/fetch/metadata/generated/css-font-face.https.sub.tentative.html b/fetch/metadata/generated/css-font-face.https.sub.tentative.html new file mode 100644 index 00000000000000..7bfafad095021c --- /dev/null +++ b/fetch/metadata/generated/css-font-face.https.sub.tentative.html @@ -0,0 +1,186 @@ + + + + + HTTP headers on request for CSS font-face + + + + + + + diff --git a/fetch/metadata/generated/css-font-face.sub.tentative.html b/fetch/metadata/generated/css-font-face.sub.tentative.html new file mode 100644 index 00000000000000..f009e1ba9e6a06 --- /dev/null +++ b/fetch/metadata/generated/css-font-face.sub.tentative.html @@ -0,0 +1,76 @@ + + + + + HTTP headers on request for CSS font-face + + + + + + + diff --git a/fetch/metadata/generated/css-images.https.sub.tentative.html b/fetch/metadata/generated/css-images.https.sub.tentative.html new file mode 100644 index 00000000000000..a4d5aab19987a2 --- /dev/null +++ b/fetch/metadata/generated/css-images.https.sub.tentative.html @@ -0,0 +1,1084 @@ + + + + + + HTTP headers on request for CSS image-accepting properties + + + + + + + diff --git a/fetch/metadata/generated/css-images.sub.tentative.html b/fetch/metadata/generated/css-images.sub.tentative.html new file mode 100644 index 00000000000000..f9677aae1cb27f --- /dev/null +++ b/fetch/metadata/generated/css-images.sub.tentative.html @@ -0,0 +1,258 @@ + + + + + HTTP headers on request for CSS image-accepting properties + + + + + + + diff --git a/fetch/metadata/generated/element-a.https.sub.html b/fetch/metadata/generated/element-a.https.sub.html new file mode 100644 index 00000000000000..6e2c6d7a7a429f --- /dev/null +++ b/fetch/metadata/generated/element-a.https.sub.html @@ -0,0 +1,397 @@ + + + + + + HTTP headers on request for HTML "a" element navigation + + + + + + + + diff --git a/fetch/metadata/generated/element-a.sub.html b/fetch/metadata/generated/element-a.sub.html new file mode 100644 index 00000000000000..227e3eff1c9f3a --- /dev/null +++ b/fetch/metadata/generated/element-a.sub.html @@ -0,0 +1,101 @@ + + + + + HTTP headers on request for HTML "a" element navigation + + + + + + + diff --git a/fetch/metadata/generated/element-area.https.sub.html b/fetch/metadata/generated/element-area.https.sub.html new file mode 100644 index 00000000000000..e1a1574e632ac5 --- /dev/null +++ b/fetch/metadata/generated/element-area.https.sub.html @@ -0,0 +1,397 @@ + + + + + + HTTP headers on request for HTML "area" element navigation + + + + + + + + diff --git a/fetch/metadata/generated/element-area.sub.html b/fetch/metadata/generated/element-area.sub.html new file mode 100644 index 00000000000000..1efb75bf70d733 --- /dev/null +++ b/fetch/metadata/generated/element-area.sub.html @@ -0,0 +1,101 @@ + + + + + HTTP headers on request for HTML "area" element navigation + + + + + + + diff --git a/fetch/metadata/generated/element-audio.https.sub.html b/fetch/metadata/generated/element-audio.https.sub.html new file mode 100644 index 00000000000000..273430671b937e --- /dev/null +++ b/fetch/metadata/generated/element-audio.https.sub.html @@ -0,0 +1,269 @@ + + + + + HTTP headers on request for HTML "audio" element source + + + + + + + diff --git a/fetch/metadata/generated/element-audio.sub.html b/fetch/metadata/generated/element-audio.sub.html new file mode 100644 index 00000000000000..9938f033e0637b --- /dev/null +++ b/fetch/metadata/generated/element-audio.sub.html @@ -0,0 +1,73 @@ + + + + + HTTP headers on request for HTML "audio" element source + + + + + + + diff --git a/fetch/metadata/generated/element-embed.https.sub.html b/fetch/metadata/generated/element-embed.https.sub.html new file mode 100644 index 00000000000000..d234bf2e40195e --- /dev/null +++ b/fetch/metadata/generated/element-embed.https.sub.html @@ -0,0 +1,180 @@ + + + + + HTTP headers on request for HTML "embed" element source + + + + + + + diff --git a/fetch/metadata/generated/element-embed.sub.html b/fetch/metadata/generated/element-embed.sub.html new file mode 100644 index 00000000000000..18313a823cdb97 --- /dev/null +++ b/fetch/metadata/generated/element-embed.sub.html @@ -0,0 +1,70 @@ + + + + + HTTP headers on request for HTML "embed" element source + + + + + + + diff --git a/fetch/metadata/generated/element-frame.https.sub.html b/fetch/metadata/generated/element-frame.https.sub.html new file mode 100644 index 00000000000000..2ab90f9f22fdda --- /dev/null +++ b/fetch/metadata/generated/element-frame.https.sub.html @@ -0,0 +1,248 @@ + + + + + HTTP headers on request for HTML "frame" element source + + + + + + + + diff --git a/fetch/metadata/generated/element-frame.sub.html b/fetch/metadata/generated/element-frame.sub.html new file mode 100644 index 00000000000000..49ae054005eec1 --- /dev/null +++ b/fetch/metadata/generated/element-frame.sub.html @@ -0,0 +1,82 @@ + + + + + HTTP headers on request for HTML "frame" element source + + + + + + + diff --git a/fetch/metadata/generated/element-iframe.https.sub.html b/fetch/metadata/generated/element-iframe.https.sub.html new file mode 100644 index 00000000000000..b73efbb3ad81c1 --- /dev/null +++ b/fetch/metadata/generated/element-iframe.https.sub.html @@ -0,0 +1,248 @@ + + + + + HTTP headers on request for HTML "frame" element source + + + + + + + + diff --git a/fetch/metadata/generated/element-iframe.sub.html b/fetch/metadata/generated/element-iframe.sub.html new file mode 100644 index 00000000000000..5d114c62a3d09f --- /dev/null +++ b/fetch/metadata/generated/element-iframe.sub.html @@ -0,0 +1,82 @@ + + + + + HTTP headers on request for HTML "frame" element source + + + + + + + diff --git a/fetch/metadata/generated/element-img-environment-change.https.sub.html b/fetch/metadata/generated/element-img-environment-change.https.sub.html new file mode 100644 index 00000000000000..cf4a41e4a5c8b2 --- /dev/null +++ b/fetch/metadata/generated/element-img-environment-change.https.sub.html @@ -0,0 +1,297 @@ + + + + + HTTP headers on image request triggered by change to environment + + + + + + + diff --git a/fetch/metadata/generated/element-img-environment-change.sub.html b/fetch/metadata/generated/element-img-environment-change.sub.html new file mode 100644 index 00000000000000..51fb818e0784b3 --- /dev/null +++ b/fetch/metadata/generated/element-img-environment-change.sub.html @@ -0,0 +1,102 @@ + + + + + HTTP headers on image request triggered by change to environment + + + + + + + diff --git a/fetch/metadata/generated/element-img.https.sub.html b/fetch/metadata/generated/element-img.https.sub.html new file mode 100644 index 00000000000000..d4f0977a094792 --- /dev/null +++ b/fetch/metadata/generated/element-img.https.sub.html @@ -0,0 +1,525 @@ + + + + + HTTP headers on request for HTML "img" element source + + + + + + + diff --git a/fetch/metadata/generated/element-img.sub.html b/fetch/metadata/generated/element-img.sub.html new file mode 100644 index 00000000000000..230167fc2e6375 --- /dev/null +++ b/fetch/metadata/generated/element-img.sub.html @@ -0,0 +1,120 @@ + + + + + HTTP headers on request for HTML "img" element source + + + + + + + diff --git a/fetch/metadata/generated/element-input-image.https.sub.html b/fetch/metadata/generated/element-input-image.https.sub.html new file mode 100644 index 00000000000000..cf55e4aee52218 --- /dev/null +++ b/fetch/metadata/generated/element-input-image.https.sub.html @@ -0,0 +1,185 @@ + + + + + HTTP headers on request for HTML "input" element with type="button" + + + + + + + diff --git a/fetch/metadata/generated/element-input-image.sub.html b/fetch/metadata/generated/element-input-image.sub.html new file mode 100644 index 00000000000000..d21f9cb9ba2168 --- /dev/null +++ b/fetch/metadata/generated/element-input-image.sub.html @@ -0,0 +1,64 @@ + + + + + HTTP headers on request for HTML "input" element with type="button" + + + + + + + diff --git a/fetch/metadata/generated/element-link-icon.https.sub.html b/fetch/metadata/generated/element-link-icon.https.sub.html new file mode 100644 index 00000000000000..7e4c148b53f80f --- /dev/null +++ b/fetch/metadata/generated/element-link-icon.https.sub.html @@ -0,0 +1,307 @@ + + + + + + HTTP headers on request for HTML "link" element with rel="icon" + + + + + + + diff --git a/fetch/metadata/generated/element-link-icon.sub.html b/fetch/metadata/generated/element-link-icon.sub.html new file mode 100644 index 00000000000000..e7d2a30c3df283 --- /dev/null +++ b/fetch/metadata/generated/element-link-icon.sub.html @@ -0,0 +1,98 @@ + + + + + HTTP headers on request for HTML "link" element with rel="icon" + + + + + + + diff --git a/fetch/metadata/generated/element-meta-refresh.https.optional.sub.html b/fetch/metadata/generated/element-meta-refresh.https.optional.sub.html new file mode 100644 index 00000000000000..fe3999adec6375 --- /dev/null +++ b/fetch/metadata/generated/element-meta-refresh.https.optional.sub.html @@ -0,0 +1,224 @@ + + + + + HTTP headers on request for HTML "meta" element with http-equiv="refresh" + + + + + + + diff --git a/fetch/metadata/generated/element-meta-refresh.optional.sub.html b/fetch/metadata/generated/element-meta-refresh.optional.sub.html new file mode 100644 index 00000000000000..72a7070d3c2b40 --- /dev/null +++ b/fetch/metadata/generated/element-meta-refresh.optional.sub.html @@ -0,0 +1,81 @@ + + + + + HTTP headers on request for HTML "meta" element with http-equiv="refresh" + + + + + + + diff --git a/fetch/metadata/generated/element-picture.https.sub.html b/fetch/metadata/generated/element-picture.https.sub.html new file mode 100644 index 00000000000000..392cab61e75ffa --- /dev/null +++ b/fetch/metadata/generated/element-picture.https.sub.html @@ -0,0 +1,805 @@ + + + + + HTTP headers on request for HTML "picture" element source + + + + + + + + diff --git a/fetch/metadata/generated/element-picture.sub.html b/fetch/metadata/generated/element-picture.sub.html new file mode 100644 index 00000000000000..db9796374e3343 --- /dev/null +++ b/fetch/metadata/generated/element-picture.sub.html @@ -0,0 +1,181 @@ + + + + + HTTP headers on request for HTML "picture" element source + + + + + + + + diff --git a/fetch/metadata/generated/element-script.https.sub.html b/fetch/metadata/generated/element-script.https.sub.html new file mode 100644 index 00000000000000..6f89e014d676f6 --- /dev/null +++ b/fetch/metadata/generated/element-script.https.sub.html @@ -0,0 +1,465 @@ + + + + + HTTP headers on request for HTML "script" element source + + + + + + diff --git a/fetch/metadata/generated/element-script.sub.html b/fetch/metadata/generated/element-script.sub.html new file mode 100644 index 00000000000000..6b4d86ff99c9be --- /dev/null +++ b/fetch/metadata/generated/element-script.sub.html @@ -0,0 +1,128 @@ + + + + + HTTP headers on request for HTML "script" element source + + + + + + diff --git a/fetch/metadata/generated/element-video-poster.https.sub.html b/fetch/metadata/generated/element-video-poster.https.sub.html new file mode 100644 index 00000000000000..d70dfe468cf46b --- /dev/null +++ b/fetch/metadata/generated/element-video-poster.https.sub.html @@ -0,0 +1,199 @@ + + + + + HTTP headers on request for HTML "video" element "poster" + + + + + + + diff --git a/fetch/metadata/generated/element-video-poster.sub.html b/fetch/metadata/generated/element-video-poster.sub.html new file mode 100644 index 00000000000000..e4304b0560a05d --- /dev/null +++ b/fetch/metadata/generated/element-video-poster.sub.html @@ -0,0 +1,78 @@ + + + + + HTTP headers on request for HTML "video" element "poster" + + + + + + + diff --git a/fetch/metadata/generated/element-video.https.sub.html b/fetch/metadata/generated/element-video.https.sub.html new file mode 100644 index 00000000000000..169d7c5b57c741 --- /dev/null +++ b/fetch/metadata/generated/element-video.https.sub.html @@ -0,0 +1,269 @@ + + + + + HTTP headers on request for HTML "video" element source + + + + + + + diff --git a/fetch/metadata/generated/element-video.sub.html b/fetch/metadata/generated/element-video.sub.html new file mode 100644 index 00000000000000..9db9189bb3b896 --- /dev/null +++ b/fetch/metadata/generated/element-video.sub.html @@ -0,0 +1,73 @@ + + + + + HTTP headers on request for HTML "video" element source + + + + + + + diff --git a/fetch/metadata/generated/fetch.https.sub.html b/fetch/metadata/generated/fetch.https.sub.html new file mode 100644 index 00000000000000..41488a047aefca --- /dev/null +++ b/fetch/metadata/generated/fetch.https.sub.html @@ -0,0 +1,246 @@ + + + + + HTTP headers on request using the "fetch" API + + + + + + + diff --git a/fetch/metadata/generated/fetch.sub.html b/fetch/metadata/generated/fetch.sub.html new file mode 100644 index 00000000000000..affb8e65b8d63b --- /dev/null +++ b/fetch/metadata/generated/fetch.sub.html @@ -0,0 +1,64 @@ + + + + + HTTP headers on request using the "fetch" API + + + + + + + diff --git a/fetch/metadata/generated/form-submission.https.sub.html b/fetch/metadata/generated/form-submission.https.sub.html new file mode 100644 index 00000000000000..8da1407a5641da --- /dev/null +++ b/fetch/metadata/generated/form-submission.https.sub.html @@ -0,0 +1,424 @@ + + + + + HTTP headers on request for HTML form navigation + + + + + + + + diff --git a/fetch/metadata/generated/form-submission.sub.html b/fetch/metadata/generated/form-submission.sub.html new file mode 100644 index 00000000000000..466f04ad3a4580 --- /dev/null +++ b/fetch/metadata/generated/form-submission.sub.html @@ -0,0 +1,135 @@ + + + + + HTTP headers on request for HTML form navigation + + + + + + + diff --git a/fetch/metadata/generated/header-link.https.sub.html b/fetch/metadata/generated/header-link.https.sub.html new file mode 100644 index 00000000000000..ab15ba999036e7 --- /dev/null +++ b/fetch/metadata/generated/header-link.https.sub.html @@ -0,0 +1,409 @@ + + + + + HTTP headers on request for HTTP "Link" header + + + + + + + diff --git a/fetch/metadata/generated/header-link.https.sub.tentative.html b/fetch/metadata/generated/header-link.https.sub.tentative.html new file mode 100644 index 00000000000000..b389c0aaee430f --- /dev/null +++ b/fetch/metadata/generated/header-link.https.sub.tentative.html @@ -0,0 +1,51 @@ + + + + + HTTP headers on request for HTTP "Link" header + + + + + + + diff --git a/fetch/metadata/generated/header-link.sub.html b/fetch/metadata/generated/header-link.sub.html new file mode 100644 index 00000000000000..a3a8086c4a0c81 --- /dev/null +++ b/fetch/metadata/generated/header-link.sub.html @@ -0,0 +1,124 @@ + + + + + HTTP headers on request for HTTP "Link" header + + + + + + + diff --git a/fetch/metadata/generated/header-refresh.https.optional.sub.html b/fetch/metadata/generated/header-refresh.https.optional.sub.html new file mode 100644 index 00000000000000..2ab80e75e99c3c --- /dev/null +++ b/fetch/metadata/generated/header-refresh.https.optional.sub.html @@ -0,0 +1,220 @@ + + + + + HTTP headers on request for HTTP "Refresh" header + + + + + + + diff --git a/fetch/metadata/generated/header-refresh.optional.sub.html b/fetch/metadata/generated/header-refresh.optional.sub.html new file mode 100644 index 00000000000000..5cfd613865454e --- /dev/null +++ b/fetch/metadata/generated/header-refresh.optional.sub.html @@ -0,0 +1,77 @@ + + + + + HTTP headers on request for HTTP "Refresh" header + + + + + + + diff --git a/fetch/metadata/generated/script-module-import-dynamic.https.sub.html b/fetch/metadata/generated/script-module-import-dynamic.https.sub.html new file mode 100644 index 00000000000000..85ee071c644ad5 --- /dev/null +++ b/fetch/metadata/generated/script-module-import-dynamic.https.sub.html @@ -0,0 +1,198 @@ + + + + + HTTP headers on request for dynamic ECMAScript module import + + + + + diff --git a/fetch/metadata/generated/script-module-import-dynamic.sub.html b/fetch/metadata/generated/script-module-import-dynamic.sub.html new file mode 100644 index 00000000000000..b1fd9fd6805482 --- /dev/null +++ b/fetch/metadata/generated/script-module-import-dynamic.sub.html @@ -0,0 +1,58 @@ + + + + + HTTP headers on request for dynamic ECMAScript module import + + + + + diff --git a/fetch/metadata/generated/script-module-import-static.https.sub.html b/fetch/metadata/generated/script-module-import-static.https.sub.html new file mode 100644 index 00000000000000..1880badf38596c --- /dev/null +++ b/fetch/metadata/generated/script-module-import-static.https.sub.html @@ -0,0 +1,228 @@ + + + + + HTTP headers on request for static ECMAScript module import + + + + + + diff --git a/fetch/metadata/generated/script-module-import-static.sub.html b/fetch/metadata/generated/script-module-import-static.sub.html new file mode 100644 index 00000000000000..b1b7910c16ec31 --- /dev/null +++ b/fetch/metadata/generated/script-module-import-static.sub.html @@ -0,0 +1,78 @@ + + + + + HTTP headers on request for static ECMAScript module import + + + + + + diff --git a/fetch/metadata/generated/svg-image.https.sub.html b/fetch/metadata/generated/svg-image.https.sub.html new file mode 100644 index 00000000000000..af8cd6e149b686 --- /dev/null +++ b/fetch/metadata/generated/svg-image.https.sub.html @@ -0,0 +1,307 @@ + + + + + + HTTP headers on request for SVG "image" element source + + + + + + + diff --git a/fetch/metadata/generated/svg-image.sub.html b/fetch/metadata/generated/svg-image.sub.html new file mode 100644 index 00000000000000..9446cdea5ad7a9 --- /dev/null +++ b/fetch/metadata/generated/svg-image.sub.html @@ -0,0 +1,96 @@ + + + + + HTTP headers on request for SVG "image" element source + + + + + + + diff --git a/fetch/metadata/generated/window-history.https.sub.html b/fetch/metadata/generated/window-history.https.sub.html new file mode 100644 index 00000000000000..85327d73b9db23 --- /dev/null +++ b/fetch/metadata/generated/window-history.https.sub.html @@ -0,0 +1,165 @@ + + + + + HTTP headers on request for navigation via the HTML History API + + + + + + + diff --git a/fetch/metadata/generated/window-location.https.sub.html b/fetch/metadata/generated/window-location.https.sub.html new file mode 100644 index 00000000000000..aef2060039e66e --- /dev/null +++ b/fetch/metadata/generated/window-location.https.sub.html @@ -0,0 +1,943 @@ + + + + + + HTTP headers on request for navigation via the HTML Location API + + + + + + + + diff --git a/fetch/metadata/generated/window-location.sub.html b/fetch/metadata/generated/window-location.sub.html new file mode 100644 index 00000000000000..605634460556eb --- /dev/null +++ b/fetch/metadata/generated/window-location.sub.html @@ -0,0 +1,221 @@ + + + + + HTTP headers on request for navigation via the HTML Location API + + + + + + + diff --git a/fetch/metadata/generated/worker-dedicated-constructor.https.sub.html b/fetch/metadata/generated/worker-dedicated-constructor.https.sub.html new file mode 100644 index 00000000000000..8d53b916f82983 --- /dev/null +++ b/fetch/metadata/generated/worker-dedicated-constructor.https.sub.html @@ -0,0 +1,118 @@ + + + + + HTTP headers on request for dedicated worker via the "Worker" constructor + + + + + diff --git a/fetch/metadata/generated/worker-dedicated-importscripts.https.sub.html b/fetch/metadata/generated/worker-dedicated-importscripts.https.sub.html new file mode 100644 index 00000000000000..991943daa1f76e --- /dev/null +++ b/fetch/metadata/generated/worker-dedicated-importscripts.https.sub.html @@ -0,0 +1,212 @@ + + + + + HTTP headers on request for dedicated worker via the "importScripts" API + + + + + diff --git a/fetch/metadata/generated/worker-dedicated-importscripts.sub.html b/fetch/metadata/generated/worker-dedicated-importscripts.sub.html new file mode 100644 index 00000000000000..58503a5475ddc2 --- /dev/null +++ b/fetch/metadata/generated/worker-dedicated-importscripts.sub.html @@ -0,0 +1,72 @@ + + + + + HTTP headers on request for dedicated worker via the "importScripts" API + + + + + diff --git a/fetch/metadata/redirect/cross-site-redirect.tentative.https.sub.html b/fetch/metadata/redirect/cross-site-redirect.tentative.https.sub.html deleted file mode 100644 index 827eb982e47f3f..00000000000000 --- a/fetch/metadata/redirect/cross-site-redirect.tentative.https.sub.html +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - diff --git a/fetch/metadata/redirect/multiple-redirect-cross-site.tentative.https.sub.html b/fetch/metadata/redirect/multiple-redirect-cross-site.tentative.https.sub.html deleted file mode 100644 index fc986aaf4fdcff..00000000000000 --- a/fetch/metadata/redirect/multiple-redirect-cross-site.tentative.https.sub.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - diff --git a/fetch/metadata/resources/appcache-iframe.sub.html b/fetch/metadata/resources/appcache-iframe.sub.html new file mode 100644 index 00000000000000..cea9a4feaec4d7 --- /dev/null +++ b/fetch/metadata/resources/appcache-iframe.sub.html @@ -0,0 +1,15 @@ + + + + diff --git a/fetch/metadata/resources/es-module.sub.js b/fetch/metadata/resources/es-module.sub.js new file mode 100644 index 00000000000000..f9668a3dc67807 --- /dev/null +++ b/fetch/metadata/resources/es-module.sub.js @@ -0,0 +1 @@ +import '{{GET[moduleId]}}'; diff --git a/fetch/metadata/resources/header-link.py b/fetch/metadata/resources/header-link.py new file mode 100644 index 00000000000000..990dbdaa92c6d0 --- /dev/null +++ b/fetch/metadata/resources/header-link.py @@ -0,0 +1,12 @@ +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 = [ + ('Content-Type', 'text/html'), + ('Link', '<{}>; rel={}'.format(request.GET.first('location'), request.GET.first('rel'))) + ] + return (200, headers, '') + diff --git a/fetch/metadata/resources/helper.sub.js b/fetch/metadata/resources/helper.sub.js new file mode 100644 index 00000000000000..89de32df1428f6 --- /dev/null +++ b/fetch/metadata/resources/helper.sub.js @@ -0,0 +1,65 @@ +'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 = { + sameOrigin: 'https://{{host}}:{{ports[https][0]}}', + httpOrigin: 'http://{{host}}:{{ports[http][0]}}', + crossSite: 'https://{{hosts[alt][]}}:{{ports[https][0]}}', + sameSite: 'https://{{hosts[][www]}}:{{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.sameOrigin + 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/fetch/metadata/resources/message-opener.html b/fetch/metadata/resources/message-opener.html new file mode 100644 index 00000000000000..eb2af7b250b9e7 --- /dev/null +++ b/fetch/metadata/resources/message-opener.html @@ -0,0 +1,17 @@ + diff --git a/fetch/metadata/resources/record-header.py b/fetch/metadata/resources/record-header.py index 99115475683d50..3f57f6ea01d4dc 100644 --- a/fetch/metadata/resources/record-header.py +++ b/fetch/metadata/resources/record-header.py @@ -129,3 +129,6 @@ def main(request, response): """ + if key.startswith("script"): + response.headers.set("Content-Type", "application/javascript") + return "void 0;" diff --git a/fetch/metadata/resources/record-headers.py b/fetch/metadata/resources/record-headers.py new file mode 100644 index 00000000000000..9918d034f8f63c --- /dev/null +++ b/fetch/metadata/resources/record-headers.py @@ -0,0 +1,47 @@ +import os +import uuid +import hashlib +import time +import json + +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 'requireOPTIONS' in request.GET and request.method != 'OPTIONS': + return + + if 'key' in request.GET: + key = request.GET['key'] + elif 'key' in request.POST: + key = request.POST['key'] + + ## Convert the key from String to UUID valid String ## + testId = hashlib.md5(key).hexdigest() + + ## Handle the header retrieval request ## + if 'retrieve' in request.GET: + recorded_headers = request.server.stash.take(testId) + + if recorded_headers is None: + return (204, [], '') + + return (200, [], recorded_headers) + + ## Record incoming fetch metadata header value + else: + try: + request.server.stash.put(testId, json.dumps(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("Access-Control-Allow-Origin", "*") + response.headers.set("Cache-Control", "no-cache, no-store, must-revalidate") + response.headers.set("Pragma", "no-cache") + response.headers.set("Expires", "0") + if "mime" in request.GET: + response.headers.set("Content-Type", request.GET.first("mime")) + + return request.GET.first("body", request.POST.first("body", "")) diff --git a/fetch/metadata/resources/redirectTestHelper.sub.js b/fetch/metadata/resources/redirectTestHelper.sub.js index e5547e7ac7931f..3b34000155897a 100644 --- a/fetch/metadata/resources/redirectTestHelper.sub.js +++ b/fetch/metadata/resources/redirectTestHelper.sub.js @@ -23,7 +23,7 @@ let insecureTestURL = 'http://{{host}}:{{ports[http][0]}}/fetch/metadata/'; // simulate us downgrading then upgrading again during the same redirect chain. function MultipleRedirectTo(partialPath) { let finalURL = insecureRedirectURL + encodeURIComponent(secureTestURL + partialPath); - return insecureRedirectURL + encodeURIComponent(finalURL); + return secureRedirectURL + encodeURIComponent(finalURL); } // Helper to craft an URL that will go from HTTP => HTTPS to simulate upgrading a diff --git a/fetch/metadata/tools/README.md b/fetch/metadata/tools/README.md new file mode 100644 index 00000000000000..3486eff7426bc7 --- /dev/null +++ b/fetch/metadata/tools/README.md @@ -0,0 +1,137 @@ +# 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. + +## Setup instructions + +- [Install Python 3](https://www.python.org/download/releases/3.0/) +- Install Python dependencies with the following command (issued from the + directory containing this document): + + $ pip install -r ./requirements.txt + +## Execution + +Execute the following command to generate tests, making sure to replace +`./path/to/configuration-file.yml` with a filesystem path to a valid +configuration file (described in greater detail below): + + $ python3 ./generate.py ./path/to/configuration-file.yml + +## 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/fetch/metadata/tools/fetch-metadata.conf.yml b/fetch/metadata/tools/fetch-metadata.conf.yml new file mode 100644 index 00000000000000..87cf83ed3582c4 --- /dev/null +++ b/fetch/metadata/tools/fetch-metadata.conf.yml @@ -0,0 +1,591 @@ +--- +templates: templates +output_directory: generated +cases: + # Sec-Fetch-Site - redirection from HTTP + - all_subtests: + headerName: sec-fetch-site + filename_flags: [] + common_axis: + - description: HTTPS downgrade (header not sent) + origins: [sameOrigin, httpOrigin] + expected: NULL + - description: HTTPS upgrade + origins: [httpOrigin, sameOrigin] + expected: cross-site + - description: HTTPS downgrade-upgrade + origins: [sameOrigin, httpOrigin, sameOrigin] + 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: [] + # These requests do not necessarily induce preflight requests because + # preflight requests are only issued on the basis of the origin of the + # initial request. + cors-preflight.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-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: [sameOrigin, crossSite, sameOrigin] + expected: cross-site + - description: Cross-Site -> Same Origin + origins: [crossSite, sameOrigin] + expected: cross-site + - description: Cross-Site -> Same-Site + origins: [crossSite, sameSite] + expected: cross-site + - description: Cross-Site -> Cross-Site + origins: [crossSite, crossSite] + expected: cross-site + - description: Same-Origin -> Same Origin + origins: [sameOrigin, sameOrigin] + expected: same-origin + - description: Same-Origin -> Same-Site + origins: [sameOrigin, sameSite] + expected: same-site + - description: Same-Origin -> Cross-Site + origins: [sameOrigin, crossSite] + expected: cross-site + - description: Same-Site -> Same Origin + origins: [sameSite, sameOrigin] + expected: same-site + - description: Same-Site -> Same-Site + origins: [sameSite, sameSite] + expected: same-site + - description: Same-Site -> Cross-Site + origins: [sameSite, crossSite] + expected: cross-site + template_axes: + # Unused + # These requests do not necessarily induce preflight requests because + # preflight requests are only issued on the basis of the origin of the + # initial request. + cors-preflight.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-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 } }] + 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: [sameOrigin, httpOrigin, sameOrigin] + 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-picture.sub.html: [] + element-script.sub.html: [] + fetch.sub.html: [] + header-link.sub.html: [] + script-module-import-static.sub.html: [] + script-module-import-dynamic.sub.html: [] + cors-preflight.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: [] + + 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] + cors-preflight.sub.html: + - expected: cors + 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-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 } + 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 } + 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] + cors-preflight.sub.html: + - expected: empty + 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-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 + 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 + # 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] + cors-preflight.sub.html: + - expected: NULL + 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-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 + 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 + 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/fetch/metadata/tools/generate.py b/fetch/metadata/tools/generate.py new file mode 100755 index 00000000000000..c483eb61ee1c05 --- /dev/null +++ b/fetch/metadata/tools/generate.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 + +import argparse +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 is '': + 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) 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__': + parser = argparse.ArgumentParser() + parser.add_argument( + 'config_file', + type=str, + help='Path to a YAML-formatted configuration file' + ) + main(**vars(parser.parse_args())) diff --git a/fetch/metadata/tools/requirements.txt b/fetch/metadata/tools/requirements.txt new file mode 100644 index 00000000000000..c28e0a8b96f501 --- /dev/null +++ b/fetch/metadata/tools/requirements.txt @@ -0,0 +1,3 @@ +Jinja2==2.10.3 +MarkupSafe==1.1.1 +PyYAML==5.2 diff --git a/fetch/metadata/tools/templates/appcache-manifest.sub.https.html b/fetch/metadata/tools/templates/appcache-manifest.sub.https.html new file mode 100644 index 00000000000000..0aed6e8729774d --- /dev/null +++ b/fetch/metadata/tools/templates/appcache-manifest.sub.https.html @@ -0,0 +1,63 @@ + + + + + HTTP headers on request for Appcache manifest + + + + + + + diff --git a/fetch/metadata/tools/templates/audioworklet.https.sub.html b/fetch/metadata/tools/templates/audioworklet.https.sub.html new file mode 100644 index 00000000000000..1fdc7b4859059e --- /dev/null +++ b/fetch/metadata/tools/templates/audioworklet.https.sub.html @@ -0,0 +1,52 @@ + + + + + HTTP headers on request for AudioWorklet module + + + + + + + + diff --git a/fetch/metadata/tools/templates/cors-preflight.sub.html b/fetch/metadata/tools/templates/cors-preflight.sub.html new file mode 100644 index 00000000000000..b5b4edfa13758b --- /dev/null +++ b/fetch/metadata/tools/templates/cors-preflight.sub.html @@ -0,0 +1,43 @@ + + + + + HTTP headers on CORS preflight request + + + + + + + diff --git a/fetch/metadata/tools/templates/css-font-face.sub.html b/fetch/metadata/tools/templates/css-font-face.sub.html new file mode 100644 index 00000000000000..6d9d289b53b167 --- /dev/null +++ b/fetch/metadata/tools/templates/css-font-face.sub.html @@ -0,0 +1,60 @@ + + + + + HTTP headers on request for CSS font-face + + + + + + + diff --git a/fetch/metadata/tools/templates/css-images.sub.html b/fetch/metadata/tools/templates/css-images.sub.html new file mode 100644 index 00000000000000..c0e6e0e42dbf01 --- /dev/null +++ b/fetch/metadata/tools/templates/css-images.sub.html @@ -0,0 +1,137 @@ + + + + + {%- if subtests|length > 10 %} + + {%- endif %} + HTTP headers on request for CSS image-accepting properties + + + + + + + diff --git a/fetch/metadata/tools/templates/element-a.sub.html b/fetch/metadata/tools/templates/element-a.sub.html new file mode 100644 index 00000000000000..2075988e6b11f1 --- /dev/null +++ b/fetch/metadata/tools/templates/element-a.sub.html @@ -0,0 +1,71 @@ + + + + + {%- if subtests|length > 10 %} + + {%- endif %} + HTTP headers on request for HTML "a" element navigation + + + {%- if subtests|selectattr('userActivated')|list %} + + {%- endif %} + + + + + diff --git a/fetch/metadata/tools/templates/element-area.sub.html b/fetch/metadata/tools/templates/element-area.sub.html new file mode 100644 index 00000000000000..fb3ccdd0178dd1 --- /dev/null +++ b/fetch/metadata/tools/templates/element-area.sub.html @@ -0,0 +1,71 @@ + + + + + {%- if subtests|length > 10 %} + + {%- endif %} + HTTP headers on request for HTML "area" element navigation + + + {%- if subtests|selectattr('userActivated')|list %} + + {%- endif %} + + + + + diff --git a/fetch/metadata/tools/templates/element-audio.sub.html b/fetch/metadata/tools/templates/element-audio.sub.html new file mode 100644 index 00000000000000..2ce840fde52e5b --- /dev/null +++ b/fetch/metadata/tools/templates/element-audio.sub.html @@ -0,0 +1,51 @@ + + + + + HTTP headers on request for HTML "audio" element source + + + + + + + diff --git a/fetch/metadata/tools/templates/element-embed.sub.html b/fetch/metadata/tools/templates/element-embed.sub.html new file mode 100644 index 00000000000000..37bc8ba50fa871 --- /dev/null +++ b/fetch/metadata/tools/templates/element-embed.sub.html @@ -0,0 +1,54 @@ + + + + + HTTP headers on request for HTML "embed" element source + + + + + + + diff --git a/fetch/metadata/tools/templates/element-frame.sub.html b/fetch/metadata/tools/templates/element-frame.sub.html new file mode 100644 index 00000000000000..1fcfa9b69deabc --- /dev/null +++ b/fetch/metadata/tools/templates/element-frame.sub.html @@ -0,0 +1,61 @@ + + + + + HTTP headers on request for HTML "frame" element source + + + {%- if subtests|selectattr('userActivated')|list %} + + {%- endif %} + + + + + diff --git a/fetch/metadata/tools/templates/element-iframe.sub.html b/fetch/metadata/tools/templates/element-iframe.sub.html new file mode 100644 index 00000000000000..a6175878f7dd2b --- /dev/null +++ b/fetch/metadata/tools/templates/element-iframe.sub.html @@ -0,0 +1,61 @@ + + + + + HTTP headers on request for HTML "frame" element source + + + {%- if subtests|selectattr('userActivated')|list %} + + {%- endif %} + + + + + diff --git a/fetch/metadata/tools/templates/element-img-environment-change.sub.html b/fetch/metadata/tools/templates/element-img-environment-change.sub.html new file mode 100644 index 00000000000000..5c840f54a45928 --- /dev/null +++ b/fetch/metadata/tools/templates/element-img-environment-change.sub.html @@ -0,0 +1,78 @@ + + + + + HTTP headers on image request triggered by change to environment + + + + + + + diff --git a/fetch/metadata/tools/templates/element-img.sub.html b/fetch/metadata/tools/templates/element-img.sub.html new file mode 100644 index 00000000000000..cb3d070ed7a26c --- /dev/null +++ b/fetch/metadata/tools/templates/element-img.sub.html @@ -0,0 +1,52 @@ + + + + + HTTP headers on request for HTML "img" element source + + + + + + + diff --git a/fetch/metadata/tools/templates/element-input-image.sub.html b/fetch/metadata/tools/templates/element-input-image.sub.html new file mode 100644 index 00000000000000..d0155be772942a --- /dev/null +++ b/fetch/metadata/tools/templates/element-input-image.sub.html @@ -0,0 +1,48 @@ + + + + + HTTP headers on request for HTML "input" element with type="button" + + + + + + + diff --git a/fetch/metadata/tools/templates/element-link-icon.sub.html b/fetch/metadata/tools/templates/element-link-icon.sub.html new file mode 100644 index 00000000000000..f6c2dec754aa84 --- /dev/null +++ b/fetch/metadata/tools/templates/element-link-icon.sub.html @@ -0,0 +1,75 @@ + + + + + {%- if subtests|length > 10 %} + + {%- endif %} + HTTP headers on request for HTML "link" element with rel="icon" + + + + + + + diff --git a/fetch/metadata/tools/templates/element-meta-refresh.optional.sub.html b/fetch/metadata/tools/templates/element-meta-refresh.optional.sub.html new file mode 100644 index 00000000000000..aaf91b54446bb7 --- /dev/null +++ b/fetch/metadata/tools/templates/element-meta-refresh.optional.sub.html @@ -0,0 +1,60 @@ + + + + + HTTP headers on request for HTML "meta" element with http-equiv="refresh" + + + + + + + diff --git a/fetch/metadata/tools/templates/element-picture.sub.html b/fetch/metadata/tools/templates/element-picture.sub.html new file mode 100644 index 00000000000000..4fc3f29862d958 --- /dev/null +++ b/fetch/metadata/tools/templates/element-picture.sub.html @@ -0,0 +1,101 @@ + + + + + HTTP headers on request for HTML "picture" element source + + + + + + + + diff --git a/fetch/metadata/tools/templates/element-script.sub.html b/fetch/metadata/tools/templates/element-script.sub.html new file mode 100644 index 00000000000000..97b5a0dd69d062 --- /dev/null +++ b/fetch/metadata/tools/templates/element-script.sub.html @@ -0,0 +1,54 @@ + + + + + HTTP headers on request for HTML "script" element source + + + + + + diff --git a/fetch/metadata/tools/templates/element-video-poster.sub.html b/fetch/metadata/tools/templates/element-video-poster.sub.html new file mode 100644 index 00000000000000..e3bd9ff0b68ced --- /dev/null +++ b/fetch/metadata/tools/templates/element-video-poster.sub.html @@ -0,0 +1,62 @@ + + + + + HTTP headers on request for HTML "video" element "poster" + + + + + + + diff --git a/fetch/metadata/tools/templates/element-video.sub.html b/fetch/metadata/tools/templates/element-video.sub.html new file mode 100644 index 00000000000000..8d254bca2ef0a8 --- /dev/null +++ b/fetch/metadata/tools/templates/element-video.sub.html @@ -0,0 +1,51 @@ + + + + + HTTP headers on request for HTML "video" element source + + + + + + + diff --git a/fetch/metadata/tools/templates/fetch.sub.html b/fetch/metadata/tools/templates/fetch.sub.html new file mode 100644 index 00000000000000..26defa29387d20 --- /dev/null +++ b/fetch/metadata/tools/templates/fetch.sub.html @@ -0,0 +1,42 @@ + + + + + HTTP headers on request using the "fetch" API + + + + + + + diff --git a/fetch/metadata/tools/templates/form-submission.sub.html b/fetch/metadata/tools/templates/form-submission.sub.html new file mode 100644 index 00000000000000..dab9d06f834bb7 --- /dev/null +++ b/fetch/metadata/tools/templates/form-submission.sub.html @@ -0,0 +1,85 @@ + + + + + HTTP headers on request for HTML form navigation + + + {%- if subtests|selectattr('userActivated')|list %} + + {%- endif %} + + + + + diff --git a/fetch/metadata/tools/templates/header-link.sub.html b/fetch/metadata/tools/templates/header-link.sub.html new file mode 100644 index 00000000000000..b5cf1f9696ed19 --- /dev/null +++ b/fetch/metadata/tools/templates/header-link.sub.html @@ -0,0 +1,56 @@ + + + + + HTTP headers on request for HTTP "Link" header + + + + + + + diff --git a/fetch/metadata/tools/templates/header-refresh.optional.sub.html b/fetch/metadata/tools/templates/header-refresh.optional.sub.html new file mode 100644 index 00000000000000..610302fc84bf05 --- /dev/null +++ b/fetch/metadata/tools/templates/header-refresh.optional.sub.html @@ -0,0 +1,56 @@ + + + + + HTTP headers on request for HTTP "Refresh" header + + + + + + + diff --git a/fetch/metadata/tools/templates/script-module-import-dynamic.sub.html b/fetch/metadata/tools/templates/script-module-import-dynamic.sub.html new file mode 100644 index 00000000000000..2600c3e22c294f --- /dev/null +++ b/fetch/metadata/tools/templates/script-module-import-dynamic.sub.html @@ -0,0 +1,35 @@ + + + + + HTTP headers on request for dynamic ECMAScript module import + + + + + diff --git a/fetch/metadata/tools/templates/script-module-import-static.sub.html b/fetch/metadata/tools/templates/script-module-import-static.sub.html new file mode 100644 index 00000000000000..1a89485b31fc83 --- /dev/null +++ b/fetch/metadata/tools/templates/script-module-import-static.sub.html @@ -0,0 +1,53 @@ + + + + + HTTP headers on request for static ECMAScript module import + + + + + + diff --git a/fetch/metadata/tools/templates/svg-image.sub.html b/fetch/metadata/tools/templates/svg-image.sub.html new file mode 100644 index 00000000000000..0e26db5f6e5b9c --- /dev/null +++ b/fetch/metadata/tools/templates/svg-image.sub.html @@ -0,0 +1,75 @@ + + + + + {%- if subtests|length > 10 %} + + {%- endif %} + HTTP headers on request for SVG "image" element source + + + + + + + diff --git a/fetch/metadata/tools/templates/window-history.sub.html b/fetch/metadata/tools/templates/window-history.sub.html new file mode 100644 index 00000000000000..b20efcab6cc6bc --- /dev/null +++ b/fetch/metadata/tools/templates/window-history.sub.html @@ -0,0 +1,133 @@ + + + + + {%- if subtests|length > 10 %} + + {%- endif %} + HTTP headers on request for navigation via the HTML History API + + + {%- if subtests|selectattr('userActivated')|list %} + + {%- endif %} + + + + + diff --git a/fetch/metadata/tools/templates/window-location.sub.html b/fetch/metadata/tools/templates/window-location.sub.html new file mode 100644 index 00000000000000..6fd84498d5d7d3 --- /dev/null +++ b/fetch/metadata/tools/templates/window-location.sub.html @@ -0,0 +1,127 @@ + + + + + {%- if subtests|length > 10 %} + + {%- endif %} + HTTP headers on request for navigation via the HTML Location API + + + {%- if subtests|selectattr('userActivated')|list %} + + {%- endif %} + + + + + diff --git a/fetch/metadata/tools/templates/worker-dedicated-constructor.sub.html b/fetch/metadata/tools/templates/worker-dedicated-constructor.sub.html new file mode 100644 index 00000000000000..7df5519a80176e --- /dev/null +++ b/fetch/metadata/tools/templates/worker-dedicated-constructor.sub.html @@ -0,0 +1,49 @@ + + + + + HTTP headers on request for dedicated worker via the "Worker" constructor + + + + + diff --git a/fetch/metadata/tools/templates/worker-dedicated-importscripts.sub.html b/fetch/metadata/tools/templates/worker-dedicated-importscripts.sub.html new file mode 100644 index 00000000000000..ee843ef0aa0379 --- /dev/null +++ b/fetch/metadata/tools/templates/worker-dedicated-importscripts.sub.html @@ -0,0 +1,54 @@ + + + + + HTTP headers on request for dedicated worker via the "importScripts" API + + + + + diff --git a/update-built-tests.sh b/update-built-tests.sh index a45058c18f89aa..f4138e1d13d3b0 100755 --- a/update-built-tests.sh +++ b/update-built-tests.sh @@ -8,3 +8,4 @@ offscreen-canvas/tools/build.sh python mimesniff/mime-types/resources/generated-mime-types.py python3 css/css-ui/tools/appearance-build-webkit-reftests.py python3 WebIDL/tools/generate-setlike.py +python fetch/metadata/tools/generate.py