diff options
Diffstat (limited to 'testing/web-platform/tests/fetch/redirect-navigate')
4 files changed, 265 insertions, 0 deletions
diff --git a/testing/web-platform/tests/fetch/redirect-navigate/302-found-post-handler.py b/testing/web-platform/tests/fetch/redirect-navigate/302-found-post-handler.py new file mode 100644 index 0000000000..40a224f656 --- /dev/null +++ b/testing/web-platform/tests/fetch/redirect-navigate/302-found-post-handler.py @@ -0,0 +1,15 @@ +from wptserve.utils import isomorphic_encode + +def main(request, response): + if request.method == u"POST": + response.add_required_headers = False + response.writer.write_status(302) + response.writer.write_header(b"Location", isomorphic_encode(request.url)) + response.writer.end_headers() + response.writer.write(b"") + elif request.method == u"GET": + return ([(b"Content-Type", b"text/plain")], + b"OK") + else: + return ([(b"Content-Type", b"text/plain")], + b"FAIL")
\ No newline at end of file diff --git a/testing/web-platform/tests/fetch/redirect-navigate/302-found-post.html b/testing/web-platform/tests/fetch/redirect-navigate/302-found-post.html new file mode 100644 index 0000000000..854cd329a8 --- /dev/null +++ b/testing/web-platform/tests/fetch/redirect-navigate/302-found-post.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<!-- Step 1: send POST request to a URL which will then 302 Found redirect --> +<title>HTTP 302 Found POST Navigation Test</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +async_test(function(t) { + window.addEventListener("load", function() { + var frame = document.getElementById("frame"); + var link = new URL("302-found-post-handler.py", window.location.href); + frame.contentWindow.document.body.innerHTML = '<form action="' + link.href + '" method="POST" id="form"><input name="n"></form>'; + frame.contentWindow.document.getElementById("form").submit(); + frame.addEventListener("load", t.step_func_done(function() { + assert_equals(frame.contentWindow.document.body.textContent, "OK"); + })); + }); +}, "HTTP 302 Found POST Navigation"); +</script> +<body> +<iframe id="frame" src="about:blank"></iframe> diff --git a/testing/web-platform/tests/fetch/redirect-navigate/preserve-fragment.html b/testing/web-platform/tests/fetch/redirect-navigate/preserve-fragment.html new file mode 100644 index 0000000000..682539a744 --- /dev/null +++ b/testing/web-platform/tests/fetch/redirect-navigate/preserve-fragment.html @@ -0,0 +1,202 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="UTF-8"> + <title>Ensure fragment is kept across redirects</title> + <meta name="timeout" content="long"> + <link rel=help href="https://www.w3.org/TR/cuap/#uri"> + <link rel=help href="https://tools.ietf.org/html/rfc7231#section-7.1.2"> + <link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=158420"> + <link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=24175"> + <script src="/common/get-host-info.sub.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + let frame; + let message; + + const HTTP_SAME_ORIGIN = "HTTP - SameOrigin"; + const HTTPS_SAME_ORIGIN = "HTTPS - SameOrigin"; + const HTTP_CROSS_ORIGIN = "HTTP - CrossOrigin"; + const HTTPS_CROSS_ORIGIN = "HTTPS - CrossOrigin"; + + function messageReceived(f) { + return new Promise((resolve) => { + window.addEventListener("message", (e) => { + message = e.data; + resolve(); + }, {once: true}); + f(); + }); + } + + function getHostname(navigation_type) { + switch (navigation_type) { + case HTTP_SAME_ORIGIN: + return get_host_info().HTTP_ORIGIN; + case HTTPS_SAME_ORIGIN: + return get_host_info().HTTPS_ORIGIN + case HTTP_CROSS_ORIGIN: + return get_host_info().HTTP_REMOTE_ORIGIN + case HTTPS_CROSS_ORIGIN: + return get_host_info().HTTPS_REMOTE_ORIGIN + } + + return 'nonexistent' + } + + // Turns |path| from a relative to this file path into a full URL, with + // the host being determined by one of the ORIGIN strings above. + function relativePathToFull(path, navigation_type) { + let host = getHostname(navigation_type); + + const pathname = window.location.pathname; + const base_path = pathname.substring(0, pathname.lastIndexOf('/') + 1); + + return host + base_path + path; + } + + // Constructs a URL to redirect.py which will respond with the given + // redirect status |code| to the provided |to_url|. Optionally adds on a + // |fragment|, if provided, to use in the initial request to redirect.py + function buildRedirectUrl(to_url, code, fragment) { + to_url = encodeURIComponent(to_url); + let dest = `/common/redirect.py?status=${code}&location=${to_url}`; + if (fragment) + dest = dest + '#' + fragment; + return dest; + } + + async function redirectTo(url, code, navigation_type, fragment) { + const dest = buildRedirectUrl(url, code, fragment); + await messageReceived( () => { + frame.contentWindow.location = getHostname(navigation_type) + dest; + }); + } + + async function doubleRedirectTo(url, code, navigation_type, fragment, intermediate_fragment) { + const second_redirection = buildRedirectUrl(url, code, intermediate_fragment); + const first_redirection = buildRedirectUrl(second_redirection, code, fragment); + await messageReceived( () => { + frame.contentWindow.location = getHostname(navigation_type) + first_redirection; + }); + } + + onload = () => { + frame = document.getElementById("frame"); + + // The tests in this file verify fragments are correctly propagated in + // a number of HTTP redirect scenarios. Each test is run for every + // relevant redirect status code. We also run each scenario under each + // combination of navigating to cross/same origin and using http/https. + const status_codes = [301, 302, 303, 307, 308]; + const navigation_types = [HTTP_SAME_ORIGIN, + HTTPS_SAME_ORIGIN, + HTTP_CROSS_ORIGIN, + HTTPS_CROSS_ORIGIN]; + + for (let navigation_type of navigation_types) { + // Navigate to a URL with a fragment. The URL redirects to a different + // page. Ensure we land on the redirected page with the fragment + // specified in the initial navigation's URL. + // + // Redirect chain: urlA#target -> urlB + // + for (let code of status_codes) { + promise_test(async () => { + const to_url = relativePathToFull('resources/destination.html', navigation_type); + await redirectTo(to_url, code, navigation_type, "target"); + assert_true(message.url.endsWith('#target')); + assert_equals(message.scrollY, 2000, "scrolls to fragment from initial navigation."); + }, `[${navigation_type}] Preserve fragment in ${code} redirect`); + } + + // Navigate to a URL with a fragment. The URL redirects to a different + // URL that also contains a fragment. Ensure we land on the redirected + // page using the fragment specified in the redirect response and not + // the one in the initial navigation. + // + // Redirect chain: urlA#target -> urlB#fromRedirect + // + for (let code of status_codes) { + promise_test(async () => { + const to_url = relativePathToFull('resources/destination.html#fromRedirect', navigation_type); + await redirectTo(to_url, code, navigation_type, "target"); + assert_true(message.url.endsWith('#fromRedirect'), `Unexpected fragment: ${message.url}`); + assert_equals(message.scrollY, 4000, "scrolls to fragment from redirect."); + }, `[${navigation_type}] Redirect URL fragment takes precedence in ${code} redirect`); + } + + // Perform two redirects. The initial navigation has a fragment and + // will redirect to a URL that also responds with a redirect. Ensure we + // land on the final page with the fragment from the original + // navigation. + // + // Redirect chain: urlA#target -> urlB -> urlC + // + for (let code of status_codes) { + promise_test(async () => { + const to_url = relativePathToFull('resources/destination.html', navigation_type); + await doubleRedirectTo(to_url, code, navigation_type, "target"); + assert_true(message.url.endsWith('#target'), `Unexpected fragment: ${message.url}`); + assert_equals(message.scrollY, 2000, "scrolls to fragment from initial navigation."); + }, `[${navigation_type}] Preserve fragment in multiple ${code} redirects`); + } + + // Perform two redirects. The initial navigation has a fragment and + // will redirect to a URL that also responds with a redirect. The + // second redirection to the final page also has a fragment. Ensure we + // land on the final page with the fragment from the redirection + // response URL. + // + // Redirect chain: urlA#target -> urlB -> urlC#fromRedirect + // + for (let code of status_codes) { + promise_test(async () => { + const to_url = relativePathToFull('resources/destination.html#fromRedirect', navigation_type); + await doubleRedirectTo(to_url, code, navigation_type, "target"); + assert_true(message.url.endsWith('#fromRedirect'), `Unexpected fragment: ${message.url}`); + assert_equals(message.scrollY, 4000, "scrolls to fragment from redirect."); + }, `[${navigation_type}] Destination URL fragment takes precedence in multiple ${code} redirects`); + } + + // Perform two redirects. The initial navigation has a fragment and + // will redirect to a URL that also responds with a redirect. This + // time, both redirect response have a fragment. Ensure we land on the + // final page with the fragment from the last redirection response URL. + // + // Redirect chain: urlA#target -> urlB#intermediate -> urlC#fromRedirect + // + for (let code of status_codes) { + promise_test(async () => { + const to_url = relativePathToFull('resources/destination.html#fromRedirect', navigation_type); + await doubleRedirectTo(to_url, code, navigation_type, "target", "intermediate"); + assert_true(message.url.endsWith('#fromRedirect'), `Unexpected fragment: ${message.url}`); + assert_equals(message.scrollY, 4000, "scrolls to fragment from redirect."); + }, `[${navigation_type}] Final redirect fragment takes precedence over intermediate in multiple ${code} redirects`); + } + + // Perform two redirects. The initial navigation has a fragment and + // will redirect to a URL that also responds with a redirect. The first + // redirect response has a fragment but the second doesn't. Ensure we + // land on the final page with the fragment from the first redirection + // response URL. + // + // Redirect chain: urlA#target -> urlB#fromRedirect -> urlC + // + for (let code of status_codes) { + promise_test(async () => { + const to_url = relativePathToFull('resources/destination.html', navigation_type); + await doubleRedirectTo(to_url, code, navigation_type, "target", "fromRedirect"); + assert_true(message.url.endsWith('#fromRedirect'), `Unexpected fragment: ${message.url}`); + assert_equals(message.scrollY, 4000, "scrolls to fragment from redirect."); + }, `[${navigation_type}] Preserve intermediate fragment in multiple ${code} redirects`); + } + } + } + </script> + </head> + <body> + <iframe id="frame" src=""></iframe> + </body> +</html> diff --git a/testing/web-platform/tests/fetch/redirect-navigate/resources/destination.html b/testing/web-platform/tests/fetch/redirect-navigate/resources/destination.html new file mode 100644 index 0000000000..f98c5a8cd7 --- /dev/null +++ b/testing/web-platform/tests/fetch/redirect-navigate/resources/destination.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="UTF-8"> + <style> + body { + height: 10000px; + margin: 0; + } + p { + position: absolute; + margin: 0; + } + </style> + <script> + window.onload = () => { + window.parent.postMessage({ + url: window.location.toString(), + scrollY: window.scrollY + }, "*"); + } + </script> + </head> + <body> + <p style="top: 2000px" id="target">Target</p> + <p style="top: 4000px" id="fromRedirect">Target</p> + </body> +</html> |