diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents')
182 files changed, 5841 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-1.html new file mode 100644 index 0000000000..4d2229eb51 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-1.html @@ -0,0 +1,7 @@ +<!doctype html> +<script> +onload = function() { + parent.postMessage("003-1", "*"); + setTimeout(function() {location = "003-2.html";}, 100); +} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-2.html new file mode 100644 index 0000000000..827a069479 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-2.html @@ -0,0 +1,9 @@ +<!doctype html> +003-2 +<script> +onload = function() { + parent.postMessage("003-2", "*") + setTimeout(function() {history.go(-1)}) +} +onunload = function() {location = "003-3.html"} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-3.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-3.html new file mode 100644 index 0000000000..8b26c896fd --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-3.html @@ -0,0 +1,4 @@ +<!doctype html> +<script> +parent.postMessage("003-3", "*"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003.html new file mode 100644 index 0000000000..f437150960 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003.html @@ -0,0 +1,22 @@ +<!doctype html> +<title>Navigation from unload whilst traversing history</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<iframe src="003-1.html"></iframe> +<script> +var t = async_test(); + +var pages = []; +var iframe = document.getElementsByTagName("iframe")[0]; + + +onmessage = t.step_func(function(e) { + pages.push(e.data); + if(pages.length == 3) { + assert_array_equals(pages, ["003-1", "003-2", "003-1"]); + t.done(); + iframe.parentNode.removeChild(iframe); + } +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-1.html new file mode 100644 index 0000000000..02f916fd9c --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-1.html @@ -0,0 +1,7 @@ +<!doctype html> +<script> +onload = function() { + parent.postMessage("004-1", "*"); + setTimeout(function() {location = location.href.replace("http://", "http://www.").replace("004-1.html", "004-2.html");}, 100); +} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-2.html new file mode 100644 index 0000000000..f2ef83ee1a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-2.html @@ -0,0 +1,9 @@ +<!doctype html> +003-2 +<script> +onload = function() { + parent.postMessage("004-2", "*") + setTimeout(function() {history.go(-1)}) +} +onunload = function() {location = "004-3.html"} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-3.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-3.html new file mode 100644 index 0000000000..c98711ae98 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-3.html @@ -0,0 +1,4 @@ +<!doctype html> +<script> +parent.postMessage("004-3", "*"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004.html new file mode 100644 index 0000000000..dddde4918b --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004.html @@ -0,0 +1,22 @@ +<!doctype html> +<title>Navigation from unload whilst traversing cross-origin history</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<iframe src="004-1.html"></iframe> +<script> +var t = async_test(); + +var pages = []; +var iframe = document.getElementsByTagName("iframe")[0]; + + +onmessage = t.step_func(function(e) { + pages.push(e.data); + if(pages.length == 3) { + assert_array_equals(pages, ["004-1", "004-2", "004-1"]); + t.done(); + iframe.parentNode.removeChild(iframe); + } +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/005.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/005.html new file mode 100644 index 0000000000..a87e6e9b3d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/005.html @@ -0,0 +1,16 @@ +<!doctype html> +<title>Link with onclick navigation and href navigation </title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<iframe id="test" name="test"></iframe> +<a target="test" onclick="document.getElementById('test').contentWindow.location='resources/click.html'" href="resources/href.html">Test</a> +<script> +var t = async_test(); +t.step(function() {document.links[0].click()}); +onmessage = t.step_func( + function(e) { + assert_equals(e.data, "href"); + t.done(); + }); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/006.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/006.html new file mode 100644 index 0000000000..17b3123faa --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/006.html @@ -0,0 +1,17 @@ +<!doctype html> +<title>Link with onclick form submit and href navigation </title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<iframe name="test"></iframe> +<form target="test" action="resources/click.html"></form> +<a target="test" onclick="document.forms[0].submit()" href="resources/href.html">Test</a> +<script> +var t = async_test(); +t.step(function() {document.links[0].click()}); +onmessage = t.step_func( + function(e) { + assert_equals(e.data, "href"); + t.done(); + }); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/007.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/007.html new file mode 100644 index 0000000000..dbb23ee644 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/007.html @@ -0,0 +1,16 @@ +<!doctype html> +<title>Link with onclick javascript url and href navigation </title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<iframe id="test" name="test"></iframe> +<a target="test" onclick="document.getElementById('test').contentWindow.location = 'javascript:\'abc<script>parent.postMessage("click", "*")</script>\'';" href="resources/href.html">Test</a> +<script> +var t = async_test(); +t.step(function() {document.getElementsByTagName("a")[0].click()}); +onmessage = t.step_func( + function(e) { + assert_equals(e.data, "href"); + t.done(); + }); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/008.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/008.html new file mode 100644 index 0000000000..3201d0679d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/008.html @@ -0,0 +1,17 @@ +<!doctype html> +<title>Link with onclick form submit to javascript url and href navigation </title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<iframe id="test" name="test"></iframe> +<form target="test" action="javascript:'<script>parent.postMessage("click", "*")</script>'"></form> +<a target="test" onclick="document.forms[0].submit()" href="resources/href.html">Test</a> +<script> +var t = async_test(); +t.step(function() {document.getElementsByTagName("a")[0].click()}); +onmessage = t.step_func( + function(e) { + assert_equals(e.data, "href"); + t.done(); + }); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/009.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/009.html new file mode 100644 index 0000000000..d963bed2bf --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/009.html @@ -0,0 +1,22 @@ +<!doctype html> +<title>Link with onclick form submit to javascript url with document.write and href navigation </title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<iframe id="test" name="test"></iframe> +<form target="test" action="javascript:(function() {document.write('<script>parent.postMessage("write", "*")</script>'); return '<script>parent.postMessage("click", "*")</script>'})()"></form> +<a target="test" onclick="document.forms[0].submit()" href="resources/href.html">Test</a> +<script> +var t = async_test(); +var events = []; +t.step(function() { + document.getElementsByTagName("a")[0].click()}); +onmessage = t.step_func( + function(e) { + events.push(e.data); + if (events.length === 2) { + assert_array_equals(events, ["write", "href"]); + t.done(); + } + }); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/010.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/010.html new file mode 100644 index 0000000000..606ad82f48 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/010.html @@ -0,0 +1,17 @@ +<!doctype html> +<title>Link with onclick form submit to javascript url with delayed document.write and href navigation </title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<iframe id="test" name="test"></iframe> +<form target="test" action="javascript:(function() {var x = new XMLHttpRequest(); x.open('GET', 'resources/blank.html?pipe=trickle(d2)', false); x.send(); document.write('<script>parent.postMessage("write", "*")</script>'); return '<script>parent.postMessage("click", "*")</script>'})()"></form> +<a target="test" onclick="document.forms[0].submit()" href="resources/href.html">Test</a> +<script> +var t = async_test(); +onload = t.step_func(function() {document.getElementsByTagName("a")[0].click()}); +onmessage = t.step_func( + function(e) { + assert_equals(e.data, "href"); + t.done(); + }); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/011.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/011.html new file mode 100644 index 0000000000..4d54267968 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/011.html @@ -0,0 +1,21 @@ +<!doctype html> +<title>Link with onclick navigation to javascript url with document.write and href navigation </title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<iframe id="test" name="test"></iframe> +<a target="test" onclick="javascript:(function() {document.write('<script>parent.postMessage("write", "*")</script>'); return '<script>parent.postMessage("click", "*")</script>'})()" href="resources/href.html">Test</a> +<script> +var t = async_test(); +var events = []; +t.step(function() { + document.getElementsByTagName("a")[0].click()}); +onmessage = t.step_func( + function(e) { + events.push(e.data); + if (events.length === 2) { + assert_array_equals(events, ["write", "href"]); + t.done(); + } + }); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/012.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/012.html new file mode 100644 index 0000000000..3795975d70 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/012.html @@ -0,0 +1,20 @@ +<!doctype html> +<title>Link with onclick navigation to javascript url with delayed document.write and href navigation </title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<iframe id="test" name="test"></iframe> +<!-- XXX: What is this test trying to do? It's navigating the subframe, but + doing a write() to _this_ document, and the "javascript:" in there is + completely a red herring: it's a label, not a protocol. There is no + javascript url involved here, unlike what the title claims! --> +<a target="test" onclick="javascript:(function() {var x = new XMLHttpRequest(); x.open('GET', 'blank.html?pipe=trickle(d2)', false); x.send(); document.write('write<script>parent.postMessage("write", "*")</script>'); return '<script>parent.postMessage("click", "*")</script>'})()" href="href.html">Test</a> +<script> +var t = async_test(); +t.step(function() {document.getElementsByTagName("a")[0].click()}); +onmessage = t.step_func( + function(e) { + assert_equals(e.data, "href"); + t.done(); + }); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/013.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/013.html new file mode 100644 index 0000000000..36a4521843 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/013.html @@ -0,0 +1,20 @@ +<!doctype html> +<title>Link with onclick navigation to javascript url with delayed document.write and href navigation </title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<iframe id="test" name="test"></iframe> +<a target="test" href="javascript:parent.events.push('javascript');">Test</a> +<script> +var t = async_test(); +var events = []; +t.step(function() { + document.getElementsByTagName("a")[0].click(); + events.push('after script'); +}); +onload = t.step_func(function() { + // javascript: executions are async. + assert_array_equals(events, ['after script', 'javascript']); + t.done(); +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/014.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/014.html new file mode 100644 index 0000000000..b27ca992bd --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/014.html @@ -0,0 +1,21 @@ +<!doctype html> +<title> Link with javascript onclick form submission script order </title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<iframe id="test" name="test"></iframe> +<form target="test" action="javascript:parent.events.push('submit');"></form> +<a target="test" onclick="document.forms[0].submit()">Test</a> +<script> +var t = async_test(); +var events = []; +t.step(function() { + document.getElementsByTagName("a")[0].click(); + events.push('after script'); +}); +onload = t.step_func(function() { + // javascript: executions are async. + assert_array_equals(events, ['after script', 'submit']); + t.done(); +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/015.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/015.html new file mode 100644 index 0000000000..696aaec2c8 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/015.html @@ -0,0 +1,20 @@ +<!doctype html> +<title> Link with javascript onclick and href script order </title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<iframe id="test" name="test"></iframe> +<a target="test" onclick="parent.events.push('click');" href="javascript:parent.events.push('href')">Test</a> +<script> +var t = async_test(); +var events = []; +t.step(function() { + document.getElementsByTagName("a")[0].click(); + events.push('after script'); +}); +onload = t.step_func(function() { + // javascript: executions are async. + assert_array_equals(events, ['click', 'after script', 'href']); + t.done(); +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-1.html new file mode 100644 index 0000000000..50a9a50fa0 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-1.html @@ -0,0 +1,32 @@ +<!doctype html> +<script> +parent.postMessage(document.readyState, "*"); +let f = document.createElement("iframe"); +f.onload = function() { + parent.postMessage("stop", "*"); + window.stop(); +}; +document.documentElement.appendChild(f); + +window.addEventListener("load", (event) => { + parent.postMessage("load", "*"); +}); +window.addEventListener("error", (event) => { + parent.postMessage("error", "*"); +}); +window.addEventListener("abort", (event) => { + parent.postMessage("abort", "*"); +}); +window.addEventListener("pageshow", (event) => { + parent.postMessage("pageshow", "*"); +}); +window.addEventListener("DOMContentLoaded", (event) => { + parent.postMessage("DOMContentLoaded", "*"); +}); +document.addEventListener("readystatechange", (event) => { + if (document.readyState === "complete") { + parent.postMessage("complete", "*"); + } +}); + +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-2.html new file mode 100644 index 0000000000..966b93c51a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-2.html @@ -0,0 +1,32 @@ +<!doctype html> +<script> +parent.postMessage(document.readyState, "*"); + +window.addEventListener("load", (event) => { + parent.postMessage("load", "*"); +}); +window.addEventListener("error", (event) => { + parent.postMessage("error", "*"); +}); +window.addEventListener("abort", (event) => { + parent.postMessage("abort", "*"); +}); +window.addEventListener("pageshow", (event) => { + parent.postMessage("pageshow", "*"); +}); +window.addEventListener("DOMContentLoaded", (event) => { + parent.postMessage("DOMContentLoaded", "*"); +}); +document.addEventListener("readystatechange", (event) => { + if (document.readyState === "complete") { + parent.postMessage("complete", "*"); + } +}); + +window.setTimeout(function() { + parent.postMessage("stop", "*"); + window.stop(); +}, 100); + +</script> +<link rel="stylesheet" href="/common/slow.py"></link> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load.html new file mode 100644 index 0000000000..4a4c3df4e7 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load.html @@ -0,0 +1,30 @@ +<!doctype html> +<title>Aborting a Document load</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link rel="help" href="https://html.spec.whatwg.org/multipage/browsing-the-web.html#aborting-a-document-load"> +<div id="log"></div> +<script> +var events = []; +onmessage = function(e) { + events.push(e.data); +}; +async_test(test => { + test.step_timeout(() => { + const frame = document.querySelector('iframe'); + const child = frame.contentWindow; + assert_equals(child.document.readyState, 'complete', 'readyState is complete'); + assert_array_equals(events, ["loading", "DOMContentLoaded", "stop", "complete"], 'no load event was fired'); + events = []; + frame.src = "abort-document-load-2.html"; + + test.step_timeout(() => { + const child = frame.contentWindow; + assert_equals(child.document.readyState, 'complete', 'readyState is complete'); + assert_array_equals(events, ["loading", "DOMContentLoaded", "stop", "complete"], 'no load event was fired'); + test.done(); + }, 1000); + }, 1000); +}); +</script> +<iframe src="abort-document-load-1.html"></iframe> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/about-srcdoc-navigation-blocked.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/about-srcdoc-navigation-blocked.window.js new file mode 100644 index 0000000000..659f7321c0 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/about-srcdoc-navigation-blocked.window.js @@ -0,0 +1,45 @@ +// META: title=Navigation to about:srcdoc, not via srcdoc="", must be blocked +// META: script=../resources/helpers.js + +promise_test(async t => { + const iframe = await addSrcdocIframe(); + + iframe.contentWindow.location = "/common/blank.html"; + await waitForIframeLoad(iframe); + + iframe.contentWindow.location = "about:srcdoc"; + + // Fetching "about:srcdoc" should result in a network error, and navigating + // to a network error should produce an opaque-origin page. In particular, + // since the error page should end up being cross-origin to the parent + // frame, `contentDocument` should return `null`. + // + // If instead this results in a message because we re-loaded a srcdoc document + // from the contents of the srcdoc="" attribute, immediately fail. + await Promise.race([ + t.step_wait(() => iframe.contentDocument === null), + failOnMessage(iframe.contentWindow) + ]); +}, "Navigations to about:srcdoc via window.location must be blocked"); + +promise_test(async t => { + const iframe = await addSrcdocIframe(); + iframe.contentWindow.name = "test_frame"; + + iframe.contentWindow.location = "/common/blank.html"; + await waitForIframeLoad(iframe); + + window.open("about:srcdoc", "test_frame"); + + // Fetching "about:srcdoc" should result in a network error, and navigating + // to a network error should produce an opaque-origin page. In particular, + // since the error page should end up being cross-origin to the parent + // frame, `contentDocument` should return `null`. + // + // If instead this results in a message because we re-loaded a srcdoc document + // from the contents of the srcdoc="" attribute, immediately fail. + await Promise.race([ + t.step_wait(() => iframe.contentDocument === null), + failOnMessage(iframe.contentWindow) + ]); +}, "Navigations to about:srcdoc via window.open() must be blocked"); diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-longfragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-longfragment.html new file mode 100644 index 0000000000..8b9802f589 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-longfragment.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org"> +<link rel="help" href="https://html.spec.whatwg.org/C/#following-hyperlinks"> +<title>Anchor element with onclick form submission and href to fragment</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<!-- When an anchor element has an onclick handler which submits a form, + the anchor's navigation should occur instead of the form's navigation. + However, if the anchor has an href which is just a fragment like "#", + then the form should be submitted. Many sites rely on this behavior. --> + +<iframe name="test"></iframe> +<form target="test" action="resources/form.html"></form> +<a id="anchor" target="test" onclick="document.forms[0].submit()" href="#fragment">Test</a> + +<script> +async_test(t => { + const anchor = document.getElementById('anchor'); + t.step(() => anchor.click()); + window.onmessage = t.step_func(event => { + if (typeof event.data === 'string' && event.data.includes('navigation')) { + assert_equals(event.data, 'form navigation'); + t.done(); + } + }); +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-withpath.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-withpath.html new file mode 100644 index 0000000000..823174181e --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-withpath.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org"> +<link rel="help" href="https://html.spec.whatwg.org/C/#following-hyperlinks"> +<title>Anchor element with onclick form submission and href to fragment</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<!-- When an anchor element has an onclick handler which submits a form, + the anchor's navigation should occur instead of the form's navigation. + However, if the anchor has an href which is just a fragment like "#", + then the form should be submitted. Many sites rely on this behavior. --> + +<iframe name="test"></iframe> +<form target="test" action="resources/form.html"></form> +<a id="anchor" target="test" onclick="document.forms[0].submit()">Test</a> + +<script> +async_test(t => { + const iframe = document.querySelector('iframe'); + iframe.onload = t.step_func(() => { + const anchor = document.getElementById('anchor'); + anchor.href = '/#'; + anchor.click(); + window.onmessage = t.step_func(event => { + if (typeof event.data === 'string' && event.data.includes('navigation')) { + assert_equals(event.data, 'form navigation'); + t.done(); + } + }); + }); + + iframe.src = '/'; +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit.html new file mode 100644 index 0000000000..e0c0e6f82d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org"> +<link rel="help" href="https://html.spec.whatwg.org/C/#following-hyperlinks"> +<title>Anchor element with onclick form submission and href to fragment</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<!-- When an anchor element has an onclick handler which submits a form, + the anchor's navigation should occur instead of the form's navigation. + However, if the anchor has an href which is just a fragment like "#", + then the form should be submitted. Many sites rely on this behavior. --> + +<iframe name="test"></iframe> +<form target="test" action="resources/form.html"></form> +<a id="anchor" target="test" onclick="document.forms[0].submit()" href="#">Test</a> + +<script> +async_test(t => { + const anchor = document.getElementById('anchor'); + t.step(() => anchor.click()); + window.onmessage = t.step_func(event => { + if (typeof event.data === 'string' && event.data.includes('navigation')) { + assert_equals(event.data, 'form navigation'); + t.done(); + } + }); +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-jsurl-form-submit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-jsurl-form-submit.html new file mode 100644 index 0000000000..1bf5d3595a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-jsurl-form-submit.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org"> +<link rel="help" href="https://html.spec.whatwg.org/C/#following-hyperlinks"> +<title>Anchor element with onclick form submission and href to javascript: url</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<!-- When an anchor element has an onclick handler which submits a form, + the anchor's navigation should occur instead of the form's navigation. + However, if the anchor has an href which returns undefined, then the form + should be submitted. --> + +<iframe name="test"></iframe> +<form target="test" action="resources/form.html"></form> +<a id="anchor" target="test" onclick="document.forms[0].submit()" href="javascript:void(0)">Test</a> + +<script> +async_test(t => { + const anchor = document.getElementById('anchor'); + t.step(() => anchor.click()); + window.onmessage = t.step_func(event => { + if (typeof event.data === 'string' && event.data.includes('navigation')) { + assert_equals(event.data, 'form navigation'); + t.done(); + } + }); +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-cross-origin.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-cross-origin.window.js new file mode 100644 index 0000000000..c5bed0fd4c --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-cross-origin.window.js @@ -0,0 +1,90 @@ +// META: script=/common/get-host-info.sub.js +// META: script=resources/wait-for-messages.js + +function testNavigationFails(params) { + return async (t) => { + // Start waiting for messages before inserting the child frame, to avoid any + // race conditions. Note that this would be racy if we executed tests + // concurrently, thankfully `promise_test` executes sequentially. See also: + // https://github.com/web-platform-tests/rfcs/pull/75 + const messagesPromise = waitForMessages(1); + + // Execute the test in an iframe, so that the document executing the test + // is not navigated away mid-test in case of failure. + const child = document.createElement("iframe"); + document.body.appendChild(child); + t.add_cleanup(() => { document.body.removeChild(child); }); + + const url = new URL( + "resources/child-navigates-parent-cross-origin-inner.html", + window.location); + + // Load the grandchild iframe from a different origin. + url.host = get_host_info().REMOTE_HOST; + + for (const key in params || {}) { + url.searchParams.set(key, params[key]); + } + + const grandchild = child.contentDocument.createElement("iframe"); + grandchild.src = url; + child.contentDocument.body.appendChild(grandchild); + + const messages = await messagesPromise; + assert_array_equals(messages, ["error: SecurityError"]); + } +} + +promise_test( + testNavigationFails(), + "Child document attempts to navigate cross-origin parent via location"); + +promise_test( + testNavigationFails({ "property": "hash" }), + "Child document attempts to navigate cross-origin parent via "+ + "location.hash"); + +promise_test( + testNavigationFails({ "property": "host" }), + "Child document attempts to navigate cross-origin parent via "+ + "location.host"); + +promise_test( + testNavigationFails({ "property": "hostname" }), + "Child document attempts to navigate cross-origin parent via "+ + "location.hostname"); + +promise_test( + testNavigationFails({ "property": "href" }), + "Child document attempts to navigate cross-origin parent via "+ + "location.href"); + +promise_test( + testNavigationFails({ "property": "pathname" }), + "Child document attempts to navigate cross-origin parent via "+ + "location.pathname"); + +promise_test( + testNavigationFails({ "property": "protocol" }), + "Child document attempts to navigate cross-origin parent via "+ + "location.protocol"); + +promise_test( + testNavigationFails({ "property": "reload" }), + "Child document attempts to navigate cross-origin parent via "+ + "location.reload()"); + +promise_test( + testNavigationFails({ "property": "replace" }), + "Child document attempts to navigate cross-origin parent via "+ + "location.replace()"); + +promise_test( + testNavigationFails({ "property": "search" }), + "Child document attempts to navigate cross-origin parent via "+ + "location.search"); + +promise_test( + testNavigationFails({ "property": "xxxNonExistent" }), + "Child document attempts to navigate cross-origin parent via non-standard "+ + "location property"); diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-same-origin.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-same-origin.window.js new file mode 100644 index 0000000000..a40c412029 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-same-origin.window.js @@ -0,0 +1,24 @@ +// META: script=resources/wait-for-messages.js + +function testNavigation(url) { + return async (t) => { + // Start waiting for messages before inserting the child frame, to avoid any + // race conditions. + const messagesPromise = waitForMessages(3); + + const iframe = document.createElement("iframe"); + iframe.src = url; + document.body.appendChild(iframe); + + const messages = await messagesPromise; + assert_array_equals(messages, ["initial", "inner", "destination"]); + } +} + +promise_test( + testNavigation("resources/child-navigates-parent-location-initial.html"), + "Child document navigates same-origin parent via document.location"); + +promise_test( + testNavigation("resources/child-navigates-parent-submit-initial.html"), + "Child document navigates same-origin parent via form submission"); diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.js new file mode 100644 index 0000000000..42b4b208ee --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.js @@ -0,0 +1,8 @@ +async_test(t => { + addEventListener('message', t.step_func_done(e => { + assert_equals(e.data, 'Denied'); + })); + const w = open("resources/page-with-top-navigating-iframe.html?parent_user_gesture=true"); + t.add_cleanup(() => {w.close()}); + +}, "Cross-origin top navigation is blocked without user activation, even if the parent has user activation"); diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-without-user-activation.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-without-user-activation.window.js new file mode 100644 index 0000000000..57f0bce247 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-without-user-activation.window.js @@ -0,0 +1,8 @@ +async_test(t => { + addEventListener('message', t.step_func_done(e => { + assert_equals(e.data, 'Denied'); + })); + const w = open("resources/page-with-top-navigating-iframe.html"); + t.add_cleanup(() => {w.close()}); + +}, "Cross-origin top navigation is blocked without user activation"); diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html new file mode 100644 index 0000000000..b9108f9937 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html @@ -0,0 +1,39 @@ +<!doctype html> +<title>load event for empty iframe in relation to the event loop</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script> +setup({explicit_done:true}); +let ran = false; + +onload = function() { + let iframe = document.createElement("iframe"); + iframe.onload = function() { + test(function() { + assert_equals(ran, false, 'Expected onload to run first'); + }, "Check execution order on load handler"); + if (ran) { + done(); + } else { + ran = true; + } + }; + document.body.appendChild(iframe); + + // Nested timeout to accommodate Gecko, because the it seems + // the outer setTimeout takes its slot in the event queue right away + // but the load event task takes its slot only at the end of this script. + setTimeout(function() { + setTimeout(function() { + test(function() { + assert_equals(ran, true, 'Expected nested setTimeout to run second'); + }, "Check execution order from nested timeout"); + if (ran) { + done(); + } else { + ran = true; + } + }); + }); +}; +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment.html new file mode 100644 index 0000000000..18a6f84c9f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment.html @@ -0,0 +1,20 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Navigating to the same URL with an empty fragment aborts the navigation</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe src="empty_fragment_iframe.html"></iframe> +<script> +// If the navigation were not aborted, we would expect multiple load events +// as the page continually reloads itself. +async_test(function(t) { + var count = 0; + var iframe = document.querySelector('iframe'); + iframe.onload = t.step_func(function() { + count++; + }); + window.child_succeeded = t.step_func_done(function() { + assert_equals(count, 1); + }); +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment_iframe.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment_iframe.html new file mode 100644 index 0000000000..26b28a0d7d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment_iframe.html @@ -0,0 +1,11 @@ +<script> +var timeout; +onload = function() { + location.hash = ""; + timeout = setTimeout(function() { parent.child_succeeded() }, 2000); +}; + +onbeforeunload = function() { + clearTimeout(timeout); +} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html new file mode 100644 index 0000000000..5d7aa2b9cb --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html @@ -0,0 +1,76 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>Sequence of the checks performed against a navigation response</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +'use strict'; +const collect = (win) => { + const report = new Promise((resolve) => { + if (!win.ReportingObserver) { + return; + } + + const observer = new win.ReportingObserver(resolve); + observer.observe(); + }).then((reports) => reports[0].type); + // Although CSP also makes use of ReportingObserver, monitoring this event + // allows the test to provide value to implementations that have not yet + // integrated CSP and Reporting (as of the time of this writing, Firefox and + // Safari). + const cspViolation = new Promise((resolve) => { + win.document.addEventListener('securitypolicyviolation', () => resolve('csp-violation')); + }); + const halfASecond = new Promise((resolve) => setTimeout(() => resolve(null), 500)); + + return Promise.race([report, cspViolation, halfASecond]); +}; + +const createWindow = (t, url) => { + const win = open(url); + t.add_cleanup(() => win.close()); + return new Promise((resolve) => win.onload = () => resolve(win)); +}; + +promise_test(async (t) => { + const win = await createWindow(t, '/common/blank.html?pipe=header(content-security-policy, frame-src none)'); + const iframe = win.document.createElement('iframe'); + iframe.src = '/common/blank.html?pipe=header(x-frame-options, deny)'; + win.document.body.appendChild(iframe); + + assert_equals(await collect(win), 'csp-violation'); +}, 'CSP check precedes X-Frame-Options check'); + +promise_test(async (t) => { + const win = await createWindow(t, '/common/blank.html?pipe=header(content-security-policy, frame-src none)|header(cross-origin-embedder-policy, require-corp)'); + const iframe = win.document.createElement('iframe'); + iframe.src = '/common/blank.html'; + win.document.body.appendChild(iframe); + + assert_equals(await collect(win), 'csp-violation'); +}, 'CSP check precedes COEP check - CSP header first'); + +promise_test(async (t) => { + const win = await createWindow(t, '/common/blank.html?pipe=header(cross-origin-embedder-policy, require-corp)|header(content-security-policy, frame-src none)'); + const iframe = win.document.createElement('iframe'); + iframe.src = '/common/blank.html'; + win.document.body.appendChild(iframe); + + assert_equals(await collect(win), 'csp-violation'); +}, 'CSP check precedes COEP check - COEP header first'); + +promise_test(async (t) => { + const win = await createWindow(t, '/common/blank.html?pipe=header(cross-origin-embedder-policy, require-corp)'); + const iframe = win.document.createElement('iframe'); + iframe.src = '/common/blank.html?pipe=header(x-frame-options, deny)'; + win.document.body.appendChild(iframe); + + assert_equals(await collect(win), 'coep'); +}, 'COEP check precedes X-Frame-Options check'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html new file mode 100644 index 0000000000..b89d3bb54b --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html @@ -0,0 +1,178 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>Navigations on iframe without src attribute</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +/* + When an iframe is created it will contain the initial empty document. If the + src and srcdoc attribute is not set, it will stay on the initial empty + document. + These tests verifies the behavior of navigations that happen on the initial + empty document in that situation. They should all be converted to do a + replacement. +*/ +"use strict"; +const url1 = "/common/blank.html?1"; +const url2 = "/common/blank.html?2"; + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src not set, which will stay on the initial empty + // document. + const iframe = insertIframe(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with no src must not change history.length"); + + // Navigate away from the initial empty document through iframe.src. This + // should do a replacement. + iframe.src = url1; + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on document loaded by iframe with no src"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + iframe.src = url2; + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength + 1, + "history.length increases after normal navigation from non-initial empty document"); +}, "src"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src not set, which will stay on the initial empty + // document. + const iframe = insertIframe(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with no src must not change history.length"); + + // Navigate away from the initial empty document through setting + // location.href. This should do a replacement. + iframe.contentWindow.location.href = url1; + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on document loaded by iframe with no src"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + iframe.contentWindow.location.href = url2; + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength + 1, + "history.length increases after normal navigation from non-initial empty document"); +}, "location.href"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src not set, which will stay on the initial empty + // document. + const iframe = insertIframe(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with no src must not change history.length"); + + // Navigate away from the initial empty document through location.assign(). + // This should do a replacement. + iframe.contentWindow.location.assign(url1); + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on document loaded by iframe with no src"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + iframe.contentWindow.location.assign(url2); + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength + 1, + "history.length increases after normal navigation from non-initial empty document"); +}, "location.assign"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src not set, which will stay on the initial empty + // document. + const iframe = insertIframe(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with no src must not change history.length"); + + // Navigate away from the initial empty document through window.open(). + // This should do a replacement. + iframe.contentWindow.open(url1, "_self"); + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on document loaded by iframe with no src"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + iframe.contentWindow.open(url2, "_self"); + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength + 1, + "history.length increases after normal navigation from non-initial empty document"); +}, "window.open"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src not set, which will stay on the initial empty + // document. + const iframe = insertIframe(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with no src must not change history.length"); + + // Navigate away from the initial empty document through clicking an <a> + // element. This should do a replacement. + const a1 = iframe.contentDocument.createElement("a"); + a1.href = url1; + iframe.contentDocument.body.appendChild(a1); + a1.click(); + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on document loaded by iframe with no src"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + const a2 = iframe.contentDocument.createElement("a"); + a2.href = url2; + iframe.contentDocument.body.appendChild(a2); + a2.click(); + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength + 1, + "history.length increases after normal navigation from non-initial empty document"); +}, "link click"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src not set, which will stay on the initial empty + // document. + const iframe = insertIframe(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with no src must not change history.length"); + + // Navigate away from the initial empty document through form submission. + // This should do a replacement. + const form1 = iframe.contentDocument.createElement("form"); + form1.action = "/common/blank.html"; + iframe.contentDocument.body.appendChild(form1); + const input1 = iframe.contentDocument.createElement("input"); + input1.type = "hidden"; + input1.name = "1"; + form1.append(input1); + form1.submit(); + await waitForLoad(t, iframe, url1 + "="); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on document loaded by iframe with no src"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + const form2 = iframe.contentDocument.createElement("form"); + form2.action = "/common/blank.html"; + iframe.contentDocument.body.appendChild(form2); + const input2 = iframe.contentDocument.createElement("input"); + input2.type = "hidden"; + input2.name = "2"; + form2.append(input2); + form2.submit(); + await waitForLoad(t, iframe, url2 + "="); + assert_equals(history.length, startingHistoryLength + 1, + "history.length increases after normal navigation from non-initial empty document"); +}, "form submission"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-fragment.html new file mode 100644 index 0000000000..2a472f6a6d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-fragment.html @@ -0,0 +1,144 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>Fragment navigations on iframe with src set to URL that doesn't load a document (HTTP 204)</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +/* + When an iframe is created it will contain the initial empty document. If the + src is set to a URL that doesn't load a new document (e.g. it results in a + HTTP 204 response), it will stay on the initial empty document. After fragment + navigations happen on it, it should still stay on the initial empty document. + These tests verifies the behavior of navigations that happen on the initial + empty document in that situation. They should all be converted to do a + replacement. +*/ +"use strict"; +const url1 = "about:blank#1"; +const url2 = "/common/blank.html?2"; + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to URL that doesn't load a new document, so + // it will stay in the initial empty document. + const iframe = insertIframeWith204Src(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src set to URL that doesn't load a new document must not change history.length"); + + // Do a fragment navigation within the initial empty document through iframe.src. + iframe.src = url1; + await new Promise(resolve => t.step_timeout(resolve, 100)); + assert_equals(iframe.contentWindow.location.href, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after fragment navigation on initial empty document"); + + // Navigate away from the initial empty document through iframe.src. + iframe.src = url2; + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation from initial empty document"); +}, "src"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to URL that doesn't load a new document, so + // it will stay in the initial empty document. + const iframe = insertIframeWith204Src(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src set to URL that doesn't load a new document must not change history.length"); + + // Do a fragment navigation within the initial empty document through setting location.href. + // This should do a replacement. + iframe.contentWindow.location.href = url1; + await new Promise(resolve => t.step_timeout(resolve, 100)); + assert_equals(iframe.contentWindow.location.href, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after fragment navigation on initial empty document"); + + // Navigate away from the initial empty document through setting location.href. + // This should do a replacement. + iframe.contentWindow.location.href = url2; + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation from initial empty document"); +}, "location.href"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to URL that doesn't load a new document, so + // it will stay in the initial empty document. + const iframe = insertIframeWith204Src(t); + await new Promise(resolve => t.step_timeout(resolve, 100)); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src set to URL that doesn't load a new document must not change history.length"); + + // Do a fragment navigation within the initial empty document through location.assign(). + // This should do a replacement. + iframe.contentWindow.location.assign(url1); + assert_equals(iframe.contentWindow.location.href, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after fragment navigation on initial empty document"); + + // Navigate away from the initial empty document through location.assign(). + // This should do a replacement. + iframe.contentWindow.location.assign(url2); + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation from initial empty document"); +}, "location.assign"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to URL that doesn't load a new document, so + // it will stay in the initial empty document. + const iframe = insertIframeWith204Src(t); + await new Promise(resolve => t.step_timeout(resolve, 100)); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src set to URL that doesn't load a new document must not change history.length"); + + // Do a fragment navigation within the initial empty document through window.open(). + // This should do a replacement. + iframe.contentWindow.open(url1, "_self"); + assert_equals(iframe.contentWindow.location.href, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after fragment navigation on initial empty document"); + + // Navigate away from the initial empty document through window.open(). + // This should do a replacement. + iframe.contentWindow.open(url2, "_self"); + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation from initial empty document"); +}, "window.open"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to URL that doesn't load a new document, so it will stay in the initial empty document. + const iframe = insertIframeWith204Src(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src set to URL that doesn't load a new document must not change history.length"); + + // Do a fragment navigation within the initial empty document through clicking an <a> element. + // This should do a replacement. + const a1 = iframe.contentDocument.createElement("a"); + a1.href = url1; + iframe.contentDocument.body.appendChild(a1); + a1.click(); + await new Promise(resolve => t.step_timeout(resolve, 100)); + assert_equals(iframe.contentWindow.location.href, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after fragment navigation on initial empty document"); + + // Navigate away from the initial empty document through clicking an <a> element. + // This should do a replacement. + const a2 = iframe.contentDocument.createElement("a"); + a2.href = url2; + iframe.contentDocument.body.appendChild(a2); + a2.click(); + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation from initial empty document"); +}, "link click"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-pushState-replaceState.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-pushState-replaceState.html new file mode 100644 index 0000000000..8a97fd36a4 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-pushState-replaceState.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>pushState/replaceState on iframe with src set to URL that doesn't load a document (HTTP 204)</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +/* + When an iframe is created it will contain the initial empty document. If the + src is set to a URL that doesn't load a new document (e.g. it results in a + HTTP 204 response), it will stay on the initial empty document. If + history.pushState() or history.replaceState() is called on it, it should + still stay on the initial empty document. + These tests verifies the behavior of navigations that happen on the initial + empty document in that situation. They should all be converted to do a + replacement. +*/ +"use strict"; +const crossDocumentURL = "/common/blank.html?2"; + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to URL that doesn't load a new document, so + // it will stay on the initial empty document. + const iframe = insertIframeWith204Src(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src set to URL that doesn't load a new document must not change history.length"); + + // Do a history.pushState() to about:blank#foo. + let pushURL = "about:blank#foo"; + iframe.contentWindow.history.pushState({}, "title", pushURL); + assert_equals(iframe.contentWindow.location.href, pushURL); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after history.pushState() on the initial empty document"); + + // Navigate away from the initial empty document. This should do replacement. + iframe.src = crossDocumentURL; + await waitForLoad(t, iframe, crossDocumentURL); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation from initial empty document"); +}, "history.pushState"); + + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to URL that doesn't load a new document, so + // it will stay on the initial empty document. + const iframe = insertIframeWith204Src(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src set to URL that doesn't load a new document must not change history.length"); + + // Do a history.replaceState() to about:blank#foo. + let replaceURL = "about:blank#foo"; + iframe.contentWindow.history.replaceState({}, "title", replaceURL); + assert_equals(iframe.contentWindow.location.href, replaceURL); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after history.replaceState() on the initial empty document"); + + // Navigate away from the initial empty document. This should do replacement. + iframe.src = crossDocumentURL; + await waitForLoad(t, iframe, crossDocumentURL); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation from initial empty document"); +}, "history.replaceState"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204.html new file mode 100644 index 0000000000..f7bade68fb --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204.html @@ -0,0 +1,178 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>Navigations on iframe with src set to URL that doesn't load a new document</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +/* + When an iframe is created it will contain the initial empty document. If the + src is set to a URL that doesn't load a new document (e.g. it results in a + HTTP 204 response), it will stay on the initial empty document. + These tests verify the behavior of navigations that happen on the initial + empty document in that situation. They should all be converted to do a + replacement. +*/ +"use strict"; +const url1 = "/common/blank.html?1"; +const url2 = "/common/blank.html?2"; + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to URL that doesn't load a new document, so + // it will stay on the initial empty document. + const iframe = insertIframeWith204Src(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src set to URL that doesn't load a new document must not change history.length"); + + // Navigate away from the initial empty document through iframe.src. This + // should do a replacement. + iframe.src = url1; + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on document loaded by iframe with no src"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + iframe.src = url2; + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength + 1, + "history.length increases after normal navigation from non-initial empty document"); +}, "Navigating to a different document with src"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to URL that doesn't load a new document, so + // it will stay on the initial empty document. + const iframe = insertIframeWith204Src(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src set to URL that doesn't load a new document must not change history.length"); + + // Navigate away from the initial empty document through setting + // location.href. This should do a replacement. + iframe.contentWindow.location.href = url1; + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on document loaded by iframe with no src"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + iframe.contentWindow.location.href = url2; + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength + 1, + "history.length increases after normal navigation from non-initial empty document"); +}, "Navigating to a different document with location.href"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to URL that doesn't load a new document, so + // it will stay on the initial empty document. + const iframe = insertIframeWith204Src(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src set to URL that doesn't load a new document must not change history.length"); + + // Navigate away from the initial empty document through location.assign(). + // This should do a replacement. + iframe.contentWindow.location.assign(url1); + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on document loaded by iframe with no src"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + iframe.contentWindow.location.assign(url2); + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength + 1, + "history.length increases after normal navigation from non-initial empty document"); +}, "Navigating to a different document with location.assign"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to URL that doesn't load a new document, so + // it will stay on the initial empty document. + const iframe = insertIframeWith204Src(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src set to URL that doesn't load a new document must not change history.length"); + + // Navigate away from the initial empty document through window.open(). + // This should do a replacement. + iframe.contentWindow.open(url1, "_self"); + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on document loaded by iframe with no src"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + iframe.contentWindow.open(url2, "_self"); + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength + 1, + "history.length increases after normal navigation from non-initial empty document"); +}, "Navigating to a different document with window.open"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to URL that doesn't load a new document, so + // it will stay on the initial empty document. + const iframe = insertIframeWith204Src(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src set to URL that doesn't load a new document must not change history.length"); + + // Navigate away from the initial empty document through clicking an <a> + // element. This should do a replacement. + const a1 = iframe.contentDocument.createElement("a"); + a1.href = url1; + iframe.contentDocument.body.appendChild(a1); + a1.click(); + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on document loaded by iframe with no src"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + const a2 = iframe.contentDocument.createElement("a"); + a2.href = url2; + iframe.contentDocument.body.appendChild(a2); + a2.click(); + await waitForLoad(t, iframe, url2); + assert_equals(history.length, startingHistoryLength + 1, + "history.length increases after normal navigation from non-initial empty document"); +}, "Navigating to a different document with link click"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to URL that doesn't load a new document, so + // it will stay on the initial empty document. + const iframe = insertIframeWith204Src(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src set to URL that doesn't load a new document must not change history.length"); + + // Navigate away from the initial empty document through form submission. + // This should do a replacement. + const form1 = iframe.contentDocument.createElement("form"); + form1.action = "/common/blank.html"; + iframe.contentDocument.body.appendChild(form1); + const input1 = iframe.contentDocument.createElement("input"); + input1.type = "hidden"; + input1.name = "1"; + form1.append(input1); + form1.submit(); + await waitForLoad(t, iframe, url1 + "="); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on document loaded by iframe with no src"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + const form2 = iframe.contentDocument.createElement("form"); + form2.action = "/common/blank.html"; + iframe.contentDocument.body.appendChild(form2); + const input2 = iframe.contentDocument.createElement("input"); + input2.type = "hidden"; + input2.name = "2"; + form2.append(input2); + form2.submit(); + await waitForLoad(t, iframe, url2 + "="); + assert_equals(history.length, startingHistoryLength + 1, + "history.length increases after normal navigation from non-initial empty document"); +}, "Navigating to a different document with form submission"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html new file mode 100644 index 0000000000..a75257d91c --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html @@ -0,0 +1,124 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>Navigations immediately after appending iframe with src='about:blank'</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +/* + When an iframe is created with src="about:blank", it will stay on the initial + empty document. These tests verify the behavior of navigations that happen + immediately after the iframe is created, which should result in replacement. +*/ +"use strict"; +const url1 = "/common/blank.html?1"; + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to about:blank. This would trigger a + // navigation to a non-initial about:blank document. + const iframe = insertIframeWithAboutBlankSrc(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src='about:blank' must not change history.length"); + + // Trigger a navigation to url1 through the iframe's src attribute. + // The iframe should still be on the initial empty document, and the + // navigation should do replacement. + iframe.src = url1; + // Wait for the latest navigation to finish. + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on initial empty document"); +}, "Navigating to a different document with src"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to about:blank but navigate away from it immediately below. + const iframe = insertIframeWithAboutBlankSrc(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src='about:blank' must not change history.length"); + + // Navigate away from the initial empty document through setting + // location.href. The iframe should still be on the initial empty document, + // and the navigation should do replacement. + iframe.contentWindow.location.href = url1; + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on initial empty document"); +}, "Navigating to a different document with location.href"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to about:blank but navigate away from it immediately below. + const iframe = insertIframeWithAboutBlankSrc(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src='about:blank' must not change history.length"); + + // Navigate away from the initial empty document through location.assign(). + // The iframe should still be on the initial empty document, and the + // navigation should do replacement. + iframe.contentWindow.location.assign(url1); + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on initial empty document"); +}, "Navigating to a different document with location.assign"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to about:blank but navigate away from it immediately below. + const iframe = insertIframeWithAboutBlankSrc(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src='about:blank' must not change history.length"); + + // Navigate away from the initial empty document through window.open(). + // The iframe should still be on the initial empty document, and the + // navigation should do replacement. + iframe.contentWindow.open(url1, "_self"); + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on initial empty document"); +}, "Navigating to a different document with window.open"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to about:blank but navigate away from it immediately below. + const iframe = insertIframeWithAboutBlankSrc(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src='about:blank' must not change history.length"); + + // Navigate away from the initial empty document through clicking an <a> + // element. The iframe should still be on the initial empty document, and the + // navigation should do replacement. + const a = iframe.contentDocument.createElement("a"); + a.href = url1; + iframe.contentDocument.body.appendChild(a); + a.click(); + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on initial empty document"); +}, "Navigating to a different document with link click"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to about:blank but navigate away from it immediately below. + const iframe = insertIframeWithAboutBlankSrc(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src='about:blank' must not change history.length"); + + // Navigate away from the initial empty document through form submission. + // The iframe should still be on the initial empty document, and the + // navigation should do replacement. + const form = iframe.contentDocument.createElement("form"); + form.action = "/common/blank.html"; + iframe.contentDocument.body.appendChild(form); + const input = iframe.contentDocument.createElement("input"); + input.type = "hidden"; + input.name = "1"; + form.append(input); + form.submit(); + await waitForLoad(t, iframe, url1 + "="); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on initial empty document"); +}, "Navigating to a different document with form submission"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-wait-for-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-wait-for-load.html new file mode 100644 index 0000000000..b7066ce521 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-wait-for-load.html @@ -0,0 +1,134 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>Navigations after iframe with src='about:blank' finished loading</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> + /* + When an iframe is created with src="about:blank", it will stay on the initial + empty document. These tests verify the behavior of navigations that happen + immediately after the load event is fired on the iframe element, which + should result in replacement. + */ +"use strict"; +const url1 = "/common/blank.html?1"; + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to about:blank, and wait for it to finish + // loading. This would trigger and commit a navigation to a non-initial + // about:blank document. + const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src='about:blank' must not change history.length"); + + // Trigger a navigation to url1 through the iframe's src attribute. + // The iframe should still be on the initial empty document, and the + // navigation should do replacement. + iframe.src = url1; + // Wait for the latest navigation to finish. + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on initial empty document"); +}, "Navigating to a different document with src"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to about:blank, and wait for it to finish + // loading. This would trigger and commit a navigation to a non-initial + // about:blank document. + const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src='about:blank' must not change history.length"); + + // Navigate away from the initial empty document through setting + // location.href. The iframe should still be on the initial empty document, + // and the navigation should do replacement. + iframe.contentWindow.location.href = url1; + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on initial empty document"); + }, "Navigating to a different document with location.href"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to about:blank, and wait for it to finish + // loading. This would trigger and commit a navigation to a non-initial + // about:blank document. + const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src='about:blank' must not change history.length"); + + // Navigate away from the initial empty document through setting + // location.href. The iframe should still be on the initial empty document, + // and the navigation should do replacement. + iframe.contentWindow.location.href = url1; + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on initial empty document"); +}, "Navigating to a different document with location.assign"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to about:blank, and wait for it to finish + // loading. This would trigger and commit a navigation to a non-initial + // about:blank document. + const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src='about:blank' must not change history.length"); + + // Navigate away from the initial empty document through window.open(). + // The iframe should still be on the initial empty document, and the + // navigation should do replacement. + iframe.contentWindow.open(url1, "_self"); + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on initial empty document"); +}, "Navigating to a different document with window.open"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to about:blank, and wait for it to finish + // loading. This would trigger and commit a navigation to a non-initial + // about:blank document. + const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src='about:blank' must not change history.length"); + + // Navigate away from the initial empty document through clicking an <a> + // element. The iframe should still be on the initial empty document, and the + // navigation should do replacement. + const a = iframe.contentDocument.createElement("a"); + a.href = url1; + iframe.contentDocument.body.appendChild(a); + a.click(); + await waitForLoad(t, iframe, url1); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on initial empty document"); +}, "Navigating to a different document with link click"); + +promise_test(async t => { + const startingHistoryLength = history.length; + // Create an iframe with src set to about:blank which will commit an about:blank document that is not the initial empty document, and wait for it to load. + const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t); + assert_equals(history.length, startingHistoryLength, + "Inserting iframe with src='about:blank' must not change history.length"); + + // Navigate away from the initial empty document through form submission. + // The iframe should still be on the initial empty document, and the + // navigation should do replacement. + const form = iframe.contentDocument.createElement("form"); + form.action = "/common/blank.html"; + iframe.contentDocument.body.appendChild(form); + const input = iframe.contentDocument.createElement("input"); + input.type = "hidden"; + input.name = "1"; + form.append(input); + form.submit(); + await waitForLoad(t, iframe, url1 + "="); + assert_equals(history.length, startingHistoryLength, + "history.length must not change after normal navigation on initial empty document"); +}, "Navigating to a different document with form submission"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/initial-content-replacement.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/initial-content-replacement.html new file mode 100644 index 0000000000..44f890df6c --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/initial-content-replacement.html @@ -0,0 +1,86 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title> + Content synchronously added to iframe/opened window's document after creation + won't get replaced asynchronously when staying on the initial empty document +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +"use strict"; + +// Asserts the document on |w| stays the same after waiting 100ms. +function assertDocumentStaysTheSame(t, w) { + const initialDocument = w.document; + initialDocument.body.innerHTML = "foo"; + return new Promise((resolve) => { + t.step_timeout(() => { + assert_equals(w.document.body.innerHTML, "foo"); + assert_equals(w.document, initialDocument); + resolve(); + }, 100); + }); +} + +promise_test(async t => { + const iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + await assertDocumentStaysTheSame(t, iframe.contentWindow); +}, "Content synchronously added to <iframe> with no src won't get replaced"); + +promise_test(async t => { + const iframe = document.createElement("iframe"); + iframe.src = ""; + document.body.appendChild(iframe); + await assertDocumentStaysTheSame(t, iframe.contentWindow); +}, "Content synchronously added to <iframe> with src='' won't get replaced"); + +promise_test(async t => { + const iframe = document.createElement("iframe"); + iframe.src = "about:blank"; + document.body.appendChild(iframe); + await assertDocumentStaysTheSame(t, iframe.contentWindow); +}, "Content synchronously added to <iframe> with src='about:blank' won't get replaced"); + +promise_test(async t => { + const iframe = document.createElement("iframe"); + iframe.src = "about:blank#foo"; + document.body.appendChild(iframe); + await assertDocumentStaysTheSame(t, iframe.contentWindow); +}, "Content synchronously added to <iframe> with src='about:blank#foo' won't get replaced"); + +promise_test(async t => { + const iframe = document.createElement("iframe"); + iframe.src = "about:blank?foo"; + document.body.appendChild(iframe); + await assertDocumentStaysTheSame(t, iframe.contentWindow); +}, "Content synchronously added to <iframe> with src='about:blank?foo' won't get replaced"); + +promise_test(async t => { + const w = window.open(); + await assertDocumentStaysTheSame(t, w); +}, "Content synchronously added to window.open()-ed document won't get replaced"); + +promise_test(async t => { + const w = window.open(""); + await assertDocumentStaysTheSame(t, w); +}, "Content synchronously added to window.open('')-ed document won't get replaced"); + +promise_test(async t => { + const w = window.open("about:blank"); + await assertDocumentStaysTheSame(t, w); +}, "Content synchronously added to window.open('about:blank')-ed document won't get replaced"); + +promise_test(async t => { + const w = window.open("about:blank#foo"); + await assertDocumentStaysTheSame(t, w); +}, "Content synchronously added to window.open('about:blank#foo')-ed document won't get replaced"); + +promise_test(async t => { + const w = window.open("about:blank?foo"); + await assertDocumentStaysTheSame(t, w); +}, "Content synchronously added to window.open('about:blank?foo')-ed document won't get replaced"); + +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-event-iframe-element.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-event-iframe-element.html new file mode 100644 index 0000000000..0d19770cc1 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-event-iframe-element.html @@ -0,0 +1,64 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>"load" event fires on the iframe element when loading the initial empty document</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +"use strict"; + +promise_test(async t => { + const iframe = document.createElement("iframe"); + let loadCount = 0; + iframe.addEventListener("load", () => { + loadCount++; + }); + document.body.appendChild(iframe); + assert_equals(loadCount, 1); +}, "load event fires synchronously on <iframe> element created with no src"); + +promise_test(async t => { + const iframe = document.createElement("iframe"); + iframe.src = ""; + let loadCount = 0; + iframe.addEventListener("load", () => { + loadCount++; + }); + document.body.appendChild(iframe); + assert_equals(loadCount, 1); +}, "load event fires synchronously on <iframe> element created with src=''"); + +promise_test(async t => { + const iframe = document.createElement("iframe"); + iframe.src = "about:blank"; + let loadCount = 0; + iframe.addEventListener("load", () => { + loadCount++; + }); + document.body.appendChild(iframe); + assert_equals(loadCount, 1); +}, "load event fires synchronously on <iframe> element created with src='about:blank'"); + +promise_test(async t => { + const iframe = document.createElement("iframe"); + iframe.src = "about:blank#foo"; + let loadCount = 0; + iframe.addEventListener("load", () => { + loadCount++; + }); + document.body.appendChild(iframe); + assert_equals(loadCount, 1); +}, "load event fires synchronously on <iframe> element created with src='about:blank#foo'"); + +promise_test(async t => { + const iframe = document.createElement("iframe"); + iframe.src = "about:blank?foo"; + let loadCount = 0; + iframe.addEventListener("load", () => { + loadCount++; + }); + document.body.appendChild(iframe); + assert_equals(loadCount, 1); +}, "load event fires synchronously on <iframe> element created with src='about:blank?foo'"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html new file mode 100644 index 0000000000..4aea4aac81 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>"load" & "pageshow" events do not fire on contentWindow of iframe that stays on the initial empty document</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +"use strict"; + +promise_test(async t => { + const iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + return assertNoLoadAndPageshowEvent(t, iframe.contentWindow); +}, "load & pageshow event do not fire on contentWindow of <iframe> element created with no src"); + +promise_test(async t => { + const iframe = document.createElement("iframe"); + iframe.src = ""; + document.body.appendChild(iframe); + return assertNoLoadAndPageshowEvent(t, iframe.contentWindow); +}, "load & pageshow events do not fire on contentWindow of <iframe> element created with src=''"); + +promise_test(async t => { + const iframe = document.createElement("iframe"); + iframe.src = "about:blank"; + document.body.appendChild(iframe); + return assertNoLoadAndPageshowEvent(t, iframe.contentWindow); +}, "load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank'"); + +promise_test(async t => { + const iframe = document.createElement("iframe"); + iframe.src = "about:blank#foo"; + document.body.appendChild(iframe); + return assertNoLoadAndPageshowEvent(t, iframe.contentWindow); +}, "load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank#foo'"); + +promise_test(async t => { + const iframe = document.createElement("iframe"); + iframe.src = "about:blank?foo"; + document.body.appendChild(iframe); + return assertNoLoadAndPageshowEvent(t, iframe.contentWindow); +}, "load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank?foo'"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html new file mode 100644 index 0000000000..9703502f7f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>"load" and "pageshow" events don't fire on window.open() that stays on the initial empty document</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +"use strict"; + +promise_test(async t => { + const w = window.open(); + return assertNoLoadAndPageshowEvent(t, w) +}, "load event does not fire on window.open()"); + +promise_test(async t => { + const w = window.open(""); + return assertNoLoadAndPageshowEvent(t, w) +}, "load event does not fire on window.open('')"); + +promise_test(async t => { + const w = window.open("about:blank"); + return assertNoLoadAndPageshowEvent(t, w) +}, "load event does not fire on window.open('about:blank')"); + +promise_test(async t => { + const w = window.open("about:blank#foo"); + return assertNoLoadAndPageshowEvent(t, w) +}, "load event does not fire on window.open('about:blank#foo')"); + +promise_test(async t => { + const w = window.open("about:blank?foo"); + return assertNoLoadAndPageshowEvent(t, w) +}, "load event does not fire on window.open('about:blank?foo')"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/code-injector.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/code-injector.html new file mode 100644 index 0000000000..631b95f9ed --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/code-injector.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Subframe</title> + +<script> +"use strict"; +{{GET[code]}} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/helpers.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/helpers.js new file mode 100644 index 0000000000..8d9473a949 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/helpers.js @@ -0,0 +1,121 @@ +// Returns a promise that asserts the "load" and "pageshow" events are not +// fired on |target|. +function assertNoLoadAndPageshowEvent(t, target) { + target.addEventListener("load", t.unreached_func("load should not be fired")); + target.addEventListener("pageshow", t.unreached_func("pageshow should not be fired")); + return new Promise(resolve => { + // Wait 50ms to ensure events fired after asynchronous navigations are + // also captured. + setTimeout(resolve, 50); + }); +} + +const url204 = "/common/blank.html?pipe=status(204)"; +const postMessageToOpenerOnLoad = ` + window.onload = () => { + window.opener.postMessage("loaded", "*") + } + `; + +// -- Start of helpers for iframe initial empty document tests. + +// Creates an iframe with an unset src and appends it to the document. +window.insertIframe = (t) => { + const iframe = document.createElement("iframe"); + t.add_cleanup(() => iframe.remove()); + document.body.append(iframe); + return iframe; +}; + +// Creates an iframe with src set to a URL that doesn't commit a new document +// (results in a HTTP 204 response) and appends it to the document. +window.insertIframeWith204Src = (t) => { + const iframe = document.createElement("iframe"); + iframe.src = url204; + t.add_cleanup(() => iframe.remove()); + document.body.append(iframe); + return iframe; +}; + +// Creates an iframe with src="about:blank" and appends it to the document. +window.insertIframeWithAboutBlankSrc = (t) => { + const iframe = document.createElement("iframe"); + t.add_cleanup(() => iframe.remove()); + iframe.src = "about:blank"; + document.body.append(iframe); + return iframe; +}; + +// Creates an iframe with src="about:blank", appends it to the document, and +// waits for the non-initial about:blank document finished loading. +window.insertIframeWithAboutBlankSrcWaitForLoad = async (t) => { + const iframe = insertIframeWithAboutBlankSrc(t); + const aboutBlankLoad = new Promise(resolve => { + // In some browsers, the non-initial about:blank navigation commits + // asynchronously, while in other browsers, it would commit synchronously. + // This means we can't wait for the "load" event as it might have already + // ran. Instead, just wait for 100ms before resolving, as the non-initial + // about:blank navigation will most likely take less than 100 ms to commit. + t.step_timeout(resolve, 100); + }); + await aboutBlankLoad; + return iframe; +}; + +// Waits for the "load" event for |urlRelativeToThisDocument| to run on +// |iframe|. +window.waitForLoad = (t, iframe, urlRelativeToThisDocument) => { + return new Promise(resolve => { + iframe.addEventListener("load", t.step_func(() => { + assert_equals(iframe.contentWindow.location.href, (new URL(urlRelativeToThisDocument, location.href)).href); + + // Wait a bit longer to ensure all history stuff has settled, e.g. the document is "completely loaded" + // (which happens from a queued task). + setTimeout(resolve, 0); + }), { once: true }); + }); +}; + +// -- End of helpers for iframe initial empty document tests. + +// -- Start of helpers for opened windows' initial empty document tests. + +// window.open() to a URL that doesn't load a new document (results in a HTTP +// 204 response). This should create a new window that stays on the initial +// empty document. +window.windowOpen204 = (t) => { + const openedWindow = window.open(url204); + t.add_cleanup(() => openedWindow.close()); + return openedWindow; +}; + +// window.open() (no URL set). This should create a new window that stays on +// the initial empty document as it won't trigger a non-initial about:blank +// navigation. +window.windowOpenNoURL = (t) => { + const openedWindow = window.open(); + t.add_cleanup(() => openedWindow.close()); + return openedWindow; +}; + +// window.open("about:blank"). This should create a new window that stays on +// the initial empty document as it won't trigger a non-initial about:blank +// navigation. +window.windowOpenAboutBlank = (t) => { + const openedWindow = window.open("about:blank"); + t.add_cleanup(() => openedWindow.close()); + return openedWindow; +}; + +// Waits for a postMessage with data set to |message| is received on the current +// window. +window.waitForMessage = (t, message) => { + return new Promise(resolve => { + window.addEventListener("message", t.step_func((event) => { + if (event.data == message) + resolve(); + })); + }); +}; + +// -- End of helpers for opened windows' initial empty document tests. diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-fragment.html new file mode 100644 index 0000000000..bb47cd3820 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-fragment.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>Fragment navigation on initial empty document created through window.open(url-with-204-response)</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +/* + When a new window is opened through window.open() it will contain the initial + empty document. If the URL parameter is set to the URL that doesn't load a + new document (e.g. it results in a HTTP 204 response), it will stay on the + initial empty document. If fragment navigations happen, it will still stay on + the initial empty document. + These tests verify the behavior of navigations that happen on the initial + empty document in that situation. They should all be converted to do a + replacement. +*/ +"use strict"; +const url1 = "about:blank#foo"; +const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" + + encodeURIComponent(postMessageToOpenerOnLoad); + +promise_test(async t => { + // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document. + const openedWindow = windowOpen204(t); + + // Do a fragment navigation within the initial empty document through setting location.href. + // This should do a replacement. + openedWindow.location.href = url1; + await new Promise(resolve => t.step_timeout(resolve, 100)); + assert_equals(openedWindow.location.hash, "#foo"); + assert_equals(openedWindow.history.length, 1, + "history.length should not increase after fragment navigation on initial empty document"); + + // Navigate away from the initial empty document through setting location.href. + // This should do a replacement. + openedWindow.location.href = url2; + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 1, + "history.length should not increase after normal navigation away from initial empty document"); +}, "location.href"); + +promise_test(async t => { + // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document. + const openedWindow = windowOpen204(t); + + // Do a fragment navigation within the initial empty document through location.assign(). + // This should do a replacement. + openedWindow.location.assign(url1); + await new Promise(resolve => t.step_timeout(resolve, 100)); + assert_equals(openedWindow.location.hash, "#foo"); + assert_equals(openedWindow.history.length, 1, + "history.length should not increase after fragment navigation on initial empty document"); + + // Navigate away from the initial empty document through location.assign(). + // This should do a replacement. + openedWindow.location.assign(url2); + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 1, + "history.length should not increase after normal navigation away from initial empty document"); +}, "location.assign"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-pushState-replaceState.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-pushState-replaceState.html new file mode 100644 index 0000000000..b5382b189f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-pushState-replaceState.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>Fragment navigation on initial empty document created through window.open(url-with-204-response)</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +/* + When a new window is opened through window.open() it will contain the initial + empty document. If the URL parameter is set to the URL that doesn't load a + new document (e.g. it results in a HTTP 204 response), it will stay on the + initial empty document. If history.pushState() or history.replaceState() is + called on it, it should still stay on the initial empty document. + These tests verify the behavior of navigations that happen on the initial + empty document in that situation. They should all be converted to do a + replacement. +*/ +"use strict"; +const url1 = "about:blank#foo"; +const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" + + encodeURIComponent(postMessageToOpenerOnLoad); + +promise_test(async t => { + // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document. + const openedWindow = windowOpen204(t); + + // Do a history.pushState() to about:blank#foo. + let pushURL = "about:blank#foo"; + openedWindow.history.pushState({}, "title", pushURL); + assert_equals(openedWindow.location.href, pushURL); + assert_equals(history.length, 1, + "history.length must not change after history.pushState() on the initial empty document"); + + // Navigate away from the initial empty document through setting location.href. + // This should do a replacement. + openedWindow.location.href = url2; + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 1, + "history.length should not increase after normal navigation away from initial empty document"); +}, "history.pushState"); + +promise_test(async t => { + // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document. + const openedWindow = windowOpen204(t); + + // Do a history.pushState() to about:blank#foo. + let replaceURL = "about:blank#foo"; + openedWindow.history.replaceState({}, "title", replaceURL); + assert_equals(openedWindow.location.href, replaceURL); + assert_equals(history.length, 1, + "history.length must not change after history.replaceState() on the initial empty document"); + + // Navigate away from the initial empty document through location.assign(). + // This should do a replacement. + openedWindow.location.assign(url2); + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 1, + "history.length should not increase after normal navigation away from initial empty document"); +}, "history.replaceState"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204.html new file mode 100644 index 0000000000..6461b3c8fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204.html @@ -0,0 +1,81 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>Navigations on window.open() with URL that doesn't load a new document</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +/* + When a new window is opened through window.open() it will contain the initial + empty document. If the URL parameter is set to the URL that doesn't load a + new document (e.g. it results in a HTTP 204 response), it will stay on the + initial empty document. + These tests verify the behavior of navigations that happen on the initial + empty document in that situation. They should all be converted to do a + replacement. +*/ +"use strict"; +const url1 = "resources/code-injector.html?1&pipe=sub(none)&code=" + + encodeURIComponent(postMessageToOpenerOnLoad); +const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" + + encodeURIComponent(postMessageToOpenerOnLoad); + +promise_test(async t => { + // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document. + const openedWindow = windowOpen204(t); + + // Navigate away from the initial empty document through setting + // location.href. This should do a replacement. + openedWindow.location.href = url1; + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 1, + "history.length should not increase after normal navigation away from initial empty document"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + openedWindow.location.href = url2; + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 2, + "history.length should increase after normal navigation away from non-initial empty document"); +}, "location.href"); + +promise_test(async t => { + // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document. + const openedWindow = windowOpen204(t); + + // Navigate away from the initial empty document through location.assign(). + // This should do a replacement. + openedWindow.location.assign(url1); + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 1, + "history.length should not increase after normal navigation away from initial empty document"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + openedWindow.location.assign(url2); + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 2, + "history.length should increase after normal navigation away from non-initial empty document"); +}, "location.assign"); +/* +promise_test(async t => { + // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document. + const openedWindow = windowOpen204(t); + + // Navigate away from the initial empty document through setting + // window.open(). This should do a replacement. + openedWindow.open(url1, "_self"); + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 1, + "history.length should increase after normal navigation away from non-initial empty document"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + openedWindow.open(url2, "_self"); + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 2, + "history.length should increase after normal navigation away from non-initial empty document"); +}, "window.open"); +*/ +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-aboutblank.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-aboutblank.html new file mode 100644 index 0000000000..f3033d6a21 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-aboutblank.html @@ -0,0 +1,81 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>Navigations on window.open(about:blank) after waiting for it to load</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +/* + When a new window is opened through window.open() it will contain the initial + empty document. If the URL parameter is set to about:blank, it will stay on + the initial empty document (unlike iframes with src="about:blank", which will + start a navigation to a non-initial about:blank document). + These tests verify the behavior of navigations that happen on the initial + empty document in that situation. They should all be converted to do a + replacement. +*/ +"use strict"; +const url1 = "resources/code-injector.html?1&pipe=sub(none)&code=" + + encodeURIComponent(postMessageToOpenerOnLoad); +const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" + + encodeURIComponent(postMessageToOpenerOnLoad); + +promise_test(async t => { + // Open a window with URL about:blank, which will commit the + // initial empty document and stay on it. + const openedWindow = windowOpenAboutBlank(t); + + // Unlike iframe with src="about:blank", window.open("about:blank") won't + // trigger a navigation to a non-initial about:blank document, so it should + // stay on the initial empty document. To verify, wait for 100ms before + // continuing. + await new Promise((resolve) => t.step_timeout(resolve, 100)); + + // Navigate away from the initial empty document through location.href. + // This should do a replacement. + openedWindow.location.href = url1; + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 1, + "history.length should not increase after normal navigation away from initial empty document"); +}, "location.href"); + +promise_test(async t => { + // Open a window with URL about:blank, which will commit the + // initial empty document and stay on it. + const openedWindow = windowOpenAboutBlank(t); + + // Unlike iframe with src="about:blank", window.open("about:blank") won't + // trigger a navigation to a non-initial about:blank document, so it should + // stay on the initial empty document. To verify, wait for 100ms before + // continuing. + await new Promise((resolve) => t.step_timeout(resolve, 100)); + + // Navigate away from the initial empty document through location.assign(). + // This should do a replacement. + openedWindow.location.assign(url1); + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 1, + "history.length should not increase after normal navigation away from initial empty document"); +}, "location.assign"); +/* +promise_test(async t => { + // Open a window with URL about:blank, which will commit the + // initial empty document and stay on it. + const openedWindow = windowOpenAboutBlank(t); + + // Unlike iframe with src="about:blank", window.open("about:blank") won't + // trigger a navigation to a non-initial about:blank document, so it should + // stay on the initial empty document. To verify, wait for 100ms before + // continuing. + await new Promise((resolve) => t.step_timeout(resolve, 100)); + + // Navigate away from the initial empty document through window.open(). + // This should do a replacement. + openedWindow.open(url1, "_self"); + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 1, + "history.length should not increase after normal navigation away from initial empty document"); +}, "window.open"); +*/ +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-history-length.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-history-length.html new file mode 100644 index 0000000000..ab89bc4098 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-history-length.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>history.length value on window.open()-ed window</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +/* + When a new window is opened through window.open() it will contain the initial + empty document, and the history.length value should be 1. +*/ + +promise_test(async t => { + const openedWindow = windowOpenNoURL(t); + assert_equals(openedWindow.history.length, 1, + "history.length should start at 1 for newly opened window"); +}, "Starting history.length for window.open()"); + +promise_test(async t => { + const openedWindow = windowOpenAboutBlank(t); + assert_equals(openedWindow.history.length, 1, + "history.length should start at 1 for newly opened window"); +}, "Starting history.length for window.open(about:blank)"); + +promise_test(async t => { + const openedWindow = windowOpen204(t); + assert_equals(openedWindow.history.length, 1, + "history.length should start at 1 for newly opened window"); +}, "Starting history.length for window.open(url-with-204-response)"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-nourl.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-nourl.html new file mode 100644 index 0000000000..801f77ad48 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-nourl.html @@ -0,0 +1,78 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>Navigations on window.open() to URL that doesn't load a new document</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<body></body> +<script> +/* + When a new window is opened through window.open() it will contain the initial + empty document. If the URL parameter is not set, it will stay on the initial + empty document. + These tests verify the behavior of navigations that happen on the initial + empty document in that situation. They should all be converted to do a + replacement. +*/ +"use strict"; +const url1 = "resources/code-injector.html?1&pipe=sub(none)&code=" + encodeURIComponent(postMessageToOpenerOnLoad); +const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" + encodeURIComponent(postMessageToOpenerOnLoad); + +promise_test(async t => { + // Open a new window with no URL, which will stay in the initial empty document until the navigation below. + const openedWindow = windowOpenNoURL(t); + + // Navigate away from the initial empty document through setting + // location.href. This should do a replacement. + openedWindow.location.href = url1; + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 1, + "history.length should not increase after normal navigation away from initial empty document"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + openedWindow.location.href = url2; + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 2, + "history.length should increase after normal navigation away from non-initial empty document"); +}, "location.href"); + +promise_test(async t => { + // Open a new window with no URL, which will stay in the initial empty document until the navigation below. + const openedWindow = windowOpenNoURL(t); + + // Navigate away from the initial empty document through location.assign(). + // This should do a replacement. + openedWindow.location.assign(url1); + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 1, + "history.length should not increase after normal navigation away from initial empty document"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + openedWindow.location.assign(url2); + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 2, + "history.length should increase after normal navigation away from non-initial empty document"); +}, "location.assign"); +/* +promise_test(async t => { + // Open a new window with no URL, which will stay in the initial empty document until the navigation below. + const openedWindow = windowOpenNoURL(t); + + // Navigate away from the initial empty document through setting + // window.open(). This should do a replacement. + openedWindow.open(url1, "_self"); + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 1, + "history.length should not increase after normal navigation away from initial empty document"); + + // Navigate again using the same method, but this time it shouldn't do a + // replacement since it's no longer on the initial empty document. + openedWindow.open(url2, "_self"); + await waitForMessage(t, "loaded"); + assert_equals(openedWindow.history.length, 2, + "history.length should increase after normal navigation away from non-initial empty document"); +}, "window.open"); +*/ +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-string.tentative.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-string.tentative.html new file mode 100644 index 0000000000..f626a79ae6 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-string.tentative.html @@ -0,0 +1,28 @@ +<!doctype html> +<title>Aborting fetch for javascript:string navigation</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link rel="help" href="https://html.spec.whatwg.org/multipage/#navigate"> +<link rel="help" href="https://github.com/whatwg/html/issues/2590"> +<div id="log"></div> +<iframe src="support/iframe-and-links.html"></iframe> +<script> +async_test(test => { + onload = () => { + const child = document.querySelector('iframe').contentWindow; + child.document.querySelector("#slowLink").click(); + // The step below is in a timeout. The framed page can't communicate back mid-parse because that + // would involve running script, which makes that navigation "mature", and we need to do this + // before it matures. + test.step_timeout(() => { + child.document.querySelector("#javascriptStringLink").click(); + child.document.querySelector("iframe").onload = test.step_func_done(() => { + assert_false(child.childLoaded, 'child.childLoaded'); + }); + }, 100); + }; + window.javascriptStringDocLoaded = test.step_func(() => { + assert_unreached("javascript: URL doc replaced the document, should be targeted to child iframe."); + }); +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-undefined.tentative.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-undefined.tentative.html new file mode 100644 index 0000000000..80a0d27a7d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-undefined.tentative.html @@ -0,0 +1,25 @@ +<!doctype html> +<title>Not aborting fetch for javascript:undefined navigation</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link rel="help" href="https://html.spec.whatwg.org/multipage/#navigate"> +<link rel="help" href="https://github.com/whatwg/html/issues/2590"> +<div id="log"></div> +<iframe src="support/iframe-and-links.html"></iframe> +<script> +async_test(test => { + onload = () => { + const child = document.querySelector('iframe').contentWindow; + child.document.querySelector("#slowLink").click(); + // The step below is in a timeout. The framed page can't communicate back mid-parse because that + // would involve running script, which makes that navigation "mature", and we need to do this + // before it matures. + test.step_timeout(() => { + child.document.querySelector("#javascriptUndefinedLink").click(); + child.document.querySelector("iframe").onload = test.step_func_done(() => { + assert_true(child.childLoaded, 'child.childLoaded'); + }); + }, 100); + }; +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/iframe-and-links.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/iframe-and-links.html new file mode 100644 index 0000000000..545b0988ba --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/iframe-and-links.html @@ -0,0 +1,18 @@ +<!doctype html> + +<iframe name="iframe"></iframe> + +<!-- slow link's response is delayed by 1 second --> +<!-- https://wptserve.readthedocs.io/en/latest/pipes.html#trickle --> +<a target="iframe" href="set-child-loaded.html?pipe=trickle(d1)" id="slowLink">slow link</a> +<a target="iframe" href="javascript:'javascript:string <script> parent.javascriptStringDocLoaded(); </script>'" id="javascriptStringLink">javascript:string link</a> +<a target="iframe" href="javascript:undefined" id="javascriptUndefinedLink">javascript:undefined link</a> + +<script> +// set-child-loaded.html (the slow link) sets this to true. +window.childLoaded = false; + +// Do nothing when the javascript:string doc has loaded, if it's correctly targeted to the above iframe. +// However, if it replaces this document, it needs to fail the test (handled in the parent). +function javascriptStringDocLoaded() {} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/set-child-loaded.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/set-child-loaded.html new file mode 100644 index 0000000000..a4b34ad6e9 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/set-child-loaded.html @@ -0,0 +1,5 @@ +<!doctype html> +set-child-loaded.html +<script> +parent.childLoaded = true; +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-global-scope.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-global-scope.html new file mode 100644 index 0000000000..d3dd38ebed --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-global-scope.html @@ -0,0 +1,16 @@ +<!doctype html> +<meta charset=utf-8> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> + +<a id="javascript-link" href="javascript:changeStatus()">link</a> + +<script> +function changeStatus() { + t.done(); +} + +var t = async_test(function(t) { + document.querySelector("#javascript-link").click(); +}, "javascript: scheme urls should be executed in current global scope"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-load-as-html.xhtml b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-load-as-html.xhtml new file mode 100644 index 0000000000..b23bef5917 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-load-as-html.xhtml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="windows-1250"?> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="../resources/helpers.js"></script> + <meta charset="windows-1250"/> + <title>javascript: URL navigation to a string must create a HTML document using the correct properties</title> +</head> +<body> + <!-- + This document is XHTML and windows-1250 so that we can test the resulting javascript: URL document is not. + The same for the window we open. + --> + <script><![CDATA[ + promise_test(async (t) => { + const w = await openWindow("resources/xhtml-and-non-utf-8.xhtml", t); + + w.location.href = `javascript:'a string<script> + opener.postMessage({ + compatMode: document.compatMode, + contentType: document.contentType, + characterSet: document.characterSet, + doctypeIsNull: document.doctype === null + }, "*"); + <` + `/script>'`; + + const results = await waitForMessage(w); + + assert_equals(results.compatMode, "BackCompat"); + assert_equals(results.contentType, "text/html"); + assert_equals(results.characterSet, "UTF-8"); + assert_equals(results.doctypeIsNull, true); + }); + ]]></script> +</body> +</html> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-no-beforeunload.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-no-beforeunload.window.js new file mode 100644 index 0000000000..47e8f11797 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-no-beforeunload.window.js @@ -0,0 +1,80 @@ +// META: script=../resources/helpers.js + +for (const stringCompletion of [false, true]) { + const testNameSuffix = stringCompletion ? ": string completion" : ": undefined completion"; + + testNoBeforeunload( + { testRunnerWindow: "top", stringCompletion }, + async (t, urlToSet) => { + const iframe = await addIframe(); + iframe.contentWindow.location.href = urlToSet; + + return iframe.contentWindow; + }, + `Navigating an iframe via location.href to a javascript: URL must not fire beforeunload${testNameSuffix}` + ); + + testNoBeforeunload( + { testRunnerWindow: "top", stringCompletion }, + async (t, urlToSet) => { + const iframe = await addIframe(); + iframe.src = urlToSet; + + return iframe.contentWindow; + }, + `Navigating an iframe via src="" to a javascript: URL after insertion must not fire beforeunload${testNameSuffix}` + ); + + testNoBeforeunload( + { testRunnerWindow: "opener", stringCompletion }, + async (t, urlToSet) => { + const w = await openWindow("/common/blank.html", t); + w.location.href = urlToSet; + + return w; + }, + `Navigating an opened window via location.href to a javascript: URL must not fire beforeunload${testNameSuffix}` + ); + + + testNoBeforeunload( + { testRunnerWindow: "opener", stringCompletion }, + async (t, urlToSet) => { + const w = await openWindow("../resources/has-iframe.html", t); + w.frames[0].onbeforeunload = t.unreached_func("beforeunload must not fire on the iframe"); + w.location.href = urlToSet; + + return w; + }, + `Navigating an opened window with an iframe via location.href to a javascript: URL must not fire beforeunload on the iframe${testNameSuffix}` + ); +} + +function testNoBeforeunload({ testRunnerWindow, stringCompletion }, setupAndNavigateFunc, description) { + promise_test(async t => { + t.add_cleanup(() => { + delete window.resolveTestPromise; + }); + + const ranPromise = new Promise(resolve => { + window.resolveTestPromise = resolve; + }); + + const urlToSet = makeURL({ testRunnerWindow, stringCompletion }); + const w = await setupAndNavigateFunc(t, urlToSet); + w.onbeforeunload = t.unreached_func("beforeunload must not fire"); + + await ranPromise; + if (stringCompletion) { + await waitForMessage(w); + } + }, description); +} + +function makeURL({ testRunnerWindow, stringCompletion }) { + const completion = stringCompletion ? + `"a string<script>window.${testRunnerWindow}.postMessage('ready', '*');</script>";` : + `undefined;`; + + return `javascript:window.${testRunnerWindow}.resolveTestPromise();${completion};`; +} diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-query-fragment-components.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-query-fragment-components.html new file mode 100644 index 0000000000..eced9646e5 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-query-fragment-components.html @@ -0,0 +1,28 @@ +<!doctype html> +<title> javascript url with query and fragment components </title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +var a = null; +var b = null; +var c = null; +</script> + +<iframe id="a" src='javascript:"nope" ? "yep" : "what";'></iframe> +<iframe id="b" src='javascript:"wrong"; // # %0a "ok";'></iframe> +<iframe id="c" src='javascript:"%252525 ? %252525 # %252525"'></iframe> + +<script> +var t = async_test("iframes with javascript src"); +function check(id, expected) { + assert_equals( + document.getElementById(id).contentDocument.body.textContent, + expected); +} +onload = t.step_func(function() { + check("a", "yep"); + check("b", "ok"); + check("c", "%2525 ? %2525 # %2525"); + t.done(); +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-referrer.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-referrer.window.js new file mode 100644 index 0000000000..1f11429c9e --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-referrer.window.js @@ -0,0 +1,38 @@ +// META: script=../resources/helpers.js +// META: title=javascript: URL navigation to a string must create a document whose referrer is the navigation initiator + +const originalURL = location.href; + +const testCases = [ + ["unsafe-url", location.href], + ["origin", self.origin + "/"], + ["no-referrer", ""] +]; + +for (const [referrerPolicyForStartingWindowCreation, expectedReferrer] of testCases) { + promise_test(async (t) => { + const meta = document.createElement("meta"); + meta.name = "referrer"; + meta.content = referrerPolicyForStartingWindowCreation; + t.add_cleanup(() => meta.remove()); + document.head.append(meta); + + const w = await openWindow("/common/blank.html", t); + const originalReferrer = w.document.referrer; + assert_equals(originalReferrer, expectedReferrer, + "Sanity check: opened window's referrer is set correctly"); + + // Mess with the current document's URL so that the initiator URL is different. Then, if that + // shows up as the javascript: URL document's referrer, we know the navigation initiator's URL is + // being used as the referrer, which is incorrect. + history.replaceState(undefined, "", "/incorrect-referrer.html"); + t.add_cleanup(() => history.replaceState(undefined, "", originalURL)); + + w.location.href = `javascript:'a string<script>opener.postMessage(document.referrer, "*");</script>'`; + + const referrer = await waitForMessage(w); + + assert_equals(referrer, originalReferrer, + "javascript: URL-created document's referrer equals the previous document's referrer"); + }, `${referrerPolicyForStartingWindowCreation} referrer policy used to create the starting page`); +} diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html new file mode 100644 index 0000000000..3c08d29674 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html @@ -0,0 +1,58 @@ +<!doctype html> +<meta charset=windows-1252> <!-- intentionally not UTF-8 to test that the javascript: frames are forced to UTF-8 --> +<title>Test javascript URL string return values in direct and indirect (target) frame contexts.</title> +<!-- Waiting on https://github.com/whatwg/html/pull/6781 to be non-tentative. --> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div id=log></div> +<script> +const testCases = [ + [[0x41]], + [[0x80,0xFF]], + [[0x80,0xFF,0x100]], + [[0xD83D,0xDE0D]], + [[0xDE0D,0x41], [0xFFFD,0x41]] +]; + +function formatCharCodes(charCodes) { + return charCodes.map(code => code.toString(16).toUpperCase().padStart(4, '0')).join(" "); +} + +for (const [input, expected = input] of testCases) { + const javascriptURL = `javascript:String.fromCharCode(${input})`; + const output = String.fromCharCode(...expected); + + async_test(t => { + const frame = document.createElement("iframe"); + t.add_cleanup(() => frame.remove()); + frame.src = javascriptURL; + + t.step_timeout(() => { + assert_equals(frame.contentDocument.body.textContent, output); + assert_equals(frame.contentDocument.charset, "UTF-8"); + t.done(); + }, 200); + + document.body.appendChild(frame); + }, `${formatCharCodes(input)} set in src=""`); + + async_test(t => { + const frame = document.createElement("iframe"); + const href = document.createElement("a"); + t.add_cleanup(() => { frame.remove(); href.remove(); }); + frame.name = "hi" + input; + href.target = "hi" + input; + href.href = javascriptURL; + + t.step_timeout(() => { + assert_equals(frame.contentDocument.body.textContent, output); + assert_equals(frame.contentDocument.charset, "UTF-8"); + t.done(); + }, 200) + + document.body.appendChild(frame); + document.body.appendChild(href); + href.click(); + }, `${formatCharCodes(input)} set in href="" targeting a frame and clicked`); +} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling.html new file mode 100644 index 0000000000..621a8cbaec --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling.html @@ -0,0 +1,36 @@ +<!doctype html> +<meta charset=utf-8> +<title>Test that javascript: evaluation only performs a navigation to the + result when the result is a string value.</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<iframe src="javascript:'1'"></iframe> +<iframe src="javascript:1"></iframe> +<iframe src="javascript:({ toString: function() { return '1'; } })"></iframe> +<iframe src="javascript:undefined"></iframe> +<iframe src="javascript:null"></iframe> +<iframe src="javascript:true"></iframe> +<iframe src="javascript:new String('1')"></iframe> +<script> + var t = async_test(); + onload = t.step_func_done(function() { + assert_equals(frames[0].document.documentElement.textContent, + "1", "string return should cause navigation"); + // The rest of the test is disabled for now, until + // https://github.com/whatwg/html/issues/1895 gets sorted out +/* + assert_equals(frames[1].document.documentElement.textContent, + "", "number return should not cause navigation"); + assert_equals(frames[2].document.documentElement.textContent, + "", "object return should not cause navigation"); + assert_equals(frames[3].document.documentElement.textContent, + "", "undefined return should not cause navigation"); + assert_equals(frames[4].document.documentElement.textContent, + "", "null return should not cause navigation"); + assert_equals(frames[5].document.documentElement.textContent, + "", "null return should not cause navigation"); + assert_equals(frames[6].document.documentElement.textContent, + "", "String object return should not cause navigation"); +*/ + }); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-failure.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-failure.sub.html new file mode 100644 index 0000000000..a153ad3e48 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-failure.sub.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>javascript: URL security check</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<body> +<script> +"use strict"; + +const cases = [ + ["cross-origin", "http://{{hosts[][www]}}:{{ports[http][0]}}/common/blank.html"], + ["cross-origin-domain but same-origin", "/html/browsers/windows/resources/document-domain-setter.html"] +]; + +for (const [description, url] of cases) { + promise_test(async t => { + const iframe = await insertIframe(t, url); + + const unreached = t.unreached_func("message event fired"); + t.add_cleanup(() => window.removeEventListener("message", unreached)); + window.addEventListener("message", unreached); + + iframe.src = `javascript:parent.postMessage("boo", "*")`; + + // If no message was received after this time, the test passes. + await new Promise(r => t.step_timeout(r, 50)); + }, `${description}, setting src`); + + promise_test(async t => { + const iframe = await insertIframe(t, url); + + const unreached = t.unreached_func("message event fired"); + t.add_cleanup(() => window.removeEventListener("message", unreached)); + window.addEventListener("message", unreached); + + iframe.contentWindow.location.href = `javascript:parent.postMessage("boo", "*")`; + + // If no message was received after this time, the test passes. + await new Promise(r => t.step_timeout(r, 50)); + }, `${description}, setting location.href`); +} + +function insertIframe(t, url) { + return new Promise((resolve, reject) => { + const iframe = document.createElement("iframe"); + iframe.src = url; + iframe.onload = () => resolve(iframe); + iframe.onerror = () => reject(new Error("Failed to load the outer iframe")); + + t.add_cleanup(() => iframe.remove()); + + document.body.append(iframe); + }); +} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-multi-globals.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-multi-globals.sub.html new file mode 100644 index 0000000000..4b9d3b7afa --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-multi-globals.sub.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Multi-globals: which one is the initiator for the javascript: URL security check?</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<body> +<script> +"use strict"; +document.domain = "{{hosts[][]}}"; + +// These tests would fail if a different pair of origins were compared (see, e.g., the discussion in +// https://github.com/whatwg/html/issues/6514). + +promise_test(async t => { + const iframe = await insertIframe(t); + const innerIframe = iframe.contentDocument.querySelector("iframe"); + + // - incumbentNavigationOrigin = this page's origin, http://{{hosts[][]}}:{{ports[http][0]}} + // - iframe's current origin is this origin, http://{{hosts[][]}}:{{ports[http][0]}}. + // javascript:'s security check uses incumbentNavigationOrigin vs. the iframe's current origin + // so the check will pass and the result will get written. + innerIframe.src = "javascript:'test'"; + + await waitForLoad(innerIframe, "Failed to load the javascript: URL"); + + assert_equals(innerIframe.contentDocument.body.textContent, "test"); +}, "Using iframeEl.src"); + +promise_test(async t => { + const iframe = await insertIframe(t); + const innerIframe = iframe.contentDocument.querySelector("iframe"); + + // Here, https://html.spec.whatwg.org/#location-object-navigate sets the source browsing context to the + // incumbent settings object's browsing context. So incumbentNavigationOrigin = this page's origin, + // http://{{hosts[][]}}:{{ports[http][0]}}. + // + // So again, the check will pass. + + iframe.contentWindow.frames[0].location.href = "javascript:'test'"; + + await waitForLoad(innerIframe, "Failed to load the javascript: URL"); + + assert_equals(innerIframe.contentDocument.body.textContent, "test"); +}, "Using location.href"); + +function insertIframe(t) { + return new Promise((resolve, reject) => { + const iframe = document.createElement("iframe"); + iframe.src = "http://{{hosts[][www]}}:{{ports[http][0]}}/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-1.sub.html"; + iframe.onload = () => resolve(iframe); + iframe.onerror = () => reject(new Error("Failed to load the outer iframe")); + + t.add_cleanup(() => iframe.remove()); + + document.body.append(iframe); + }); +} + +function waitForLoad(iframe, errorMessage = "Failed to load iframe") { + return new Promise((resolve, reject) => { + iframe.onload = () => resolve(iframe); + iframe.onerror = () => reject(new Error(errorMessage)); + }); +} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html new file mode 100644 index 0000000000..a14a13cfd6 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>javascript: URL security check for same-origin-domain but not same-origin</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<iframe src="http://{{hosts[][www]}}:{{ports[http][0]}}/html/browsers/browsing-the-web/navigating-across-documents/resources/document-domain-set-to-site.sub.html"></iframe> +<script> +"use strict"; +document.domain = "{{host}}"; + +setup({ explicit_done: true }); + +window.onload = () => { + async_test(t => { + assert_equals(frames[0].document.body.textContent, "", "before"); + + window.onmessage = t.step_func_done(() => { + assert_equals(frames[0].document.body.textContent, "new", "after"); + }); + + frames[0].location.href = "javascript:parent.postMessage('done', '*'); 'new';"; + }); + done(); +}; +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-task-queuing.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-task-queuing.html new file mode 100644 index 0000000000..1bb05bfb19 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-task-queuing.html @@ -0,0 +1,58 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>javascript: URL task queuing</title> +<link rel="help" href="https://github.com/whatwg/html/issues/3730"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<body> +<script> +"use strict"; + +testIsAsync(() => { + const iframe = document.createElement("iframe"); + document.body.append(iframe); + iframe.contentWindow.location.href = "javascript:window.top.javascriptURLRan = true; window.top.resolveTestPromise();"; +}, `Navigating an iframe via location.href to a javascript: URL must queue a task`); + +testIsAsync(() => { + const iframe = document.createElement("iframe"); + iframe.src = "javascript:window.top.javascriptURLRan = true; window.top.resolveTestPromise();"; + document.body.append(iframe); +}, `Navigating an iframe via src="" to a javascript: URL before insertion must queue a task`); + +testIsAsync(() => { + const iframe = document.createElement("iframe"); + document.body.append(iframe); + iframe.src = "javascript:window.top.javascriptURLRan = true; window.top.resolveTestPromise();"; +}, `Navigating an iframe via src="" to a javascript: URL after insertion must queue a task`); + +testIsAsync(() => { + const w = window.open(); + w.location.href = "javascript:window.opener.javascriptURLRan = true; window.opener.resolveTestPromise();"; +}, `Navigating an opened window via location.href to a javascript: URL must queue a task`); + +testIsAsync(() => { + window.open("javascript:window.opener.javascriptURLRan = true; window.opener.resolveTestPromise();"); +}, `Navigating an opened window as part of creation to a javascript: URL must queue a task`); + +function testIsAsync(setupFunc, description) { + promise_test(async t => { + t.add_cleanup(() => { + delete window.resolveTestPromise; + delete window.javascriptURLRan; + }); + + const ranPromise = new Promise(resolve => { + window.resolveTestPromise = resolve; + }); + + setupFunc(); + + assert_equals(window.javascriptURLRan, undefined, "Must not run sync"); + + // Ensure that we do actually run the code, though. + await ranPromise; + }, description); +} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-assign.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-assign.html new file mode 100644 index 0000000000..cb2984d409 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-assign.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/context-helper.js"></script> +<script> +window.scriptToRun = 'relevantWindow.location.assign("target.html");'; + +async_test(t => { + window.addEventListener("message", t.step_func_done(function(e) { + // Base URL used for parsing a relative URL to `target.html` + // should be the base URL of the entry settings object in + // https://html.spec.whatwg.org/C/#dom-location-assign + assert_equals( + e.data.location, + new URL('target.html', entryUrl).href, + 'Base URL should use the entry settings object'); + + // `document.referrer` should reflect the source browsing context, + // which is the incumbent in + // https://html.spec.whatwg.org/C/#location-object-navigate + assert_equals( + e.data.referrer, incumbentUrl, + 'Referrer should use the incumbent settings object'); + })); +}, 'Fetch client and URL resolution for location.assign()'); +</script> +<iframe id="entry" src="entry/entry.html"></iframe> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-href.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-href.html new file mode 100644 index 0000000000..02ff214ed5 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-href.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/context-helper.js"></script> +<script> +window.scriptToRun = 'relevantWindow.location.href = "target.html";'; + +async_test(t => { + window.addEventListener("message", t.step_func_done(function(e) { + // Base URL used for parsing a relative URL to `target.html` + // should be the base URL of the entry settings object in + // https://html.spec.whatwg.org/C/#dom-location-href + assert_equals( + e.data.location, + new URL('target.html', entryUrl).href, + 'Base URL should use the entry settings object'); + + // `document.referrer` should reflect the source browsing context, + // which is the incumbent in + // https://html.spec.whatwg.org/C/#location-object-navigate + assert_equals( + e.data.referrer, incumbentUrl, + 'Referrer should use the incumbent settings object'); + })); +}, 'Fetch client and URL resolution for location.href setter'); +</script> +<iframe id="entry" src="entry/entry.html"></iframe> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location.html new file mode 100644 index 0000000000..fae17dd2ac --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/context-helper.js"></script> +<script> +window.scriptToRun = 'relevantWindow.location = "target.html";'; + +async_test(t => { + window.addEventListener("message", t.step_func_done(function(e) { + // Base URL used for parsing a relative URL to `target.html` + // should be the base URL of the entry settings object in + // https://html.spec.whatwg.org/C/#dom-location-assign + assert_equals( + e.data.location, + new URL('target.html', entryUrl).href, + 'Base URL should use the entry settings object'); + + // `document.referrer` should reflect the source browsing context, + // which is the incumbent in + // https://html.spec.whatwg.org/C/#location-object-navigate + assert_equals( + e.data.referrer, incumbentUrl, + 'Referrer should use the incumbent settings object'); + })); +}, 'Fetch client and URL resolution for location setter'); +</script> +<iframe id="entry" src="entry/entry.html"></iframe> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-window-open.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-window-open.html new file mode 100644 index 0000000000..0a391ef28e --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-window-open.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/context-helper.js"></script> +<script> +window.scriptToRun = + 'relevantWindow.open("target.html", "target");'; + +async_test(t => { + window.addEventListener("message", t.step_func_done(function(e) { + // Base URL used for parsing a relative URL to `target.html` + // should be the base URL of the entry settings object in + // https://html.spec.whatwg.org/C/#window-open-steps + assert_equals( + e.data.location, + new URL('target.html', entryUrl).href, + 'Base URL should use the entry settings object'); + + // `document.referrer` should reflect the source browsing context, + // which is the entry in + // https://html.spec.whatwg.org/C/#window-open-steps + assert_equals( + e.data.referrer, entryUrl, + 'Referrer should use the entry settings object'); + })); +}, 'Fetch client and URL resolution for window.open()'); +</script> +<iframe id="entry" src="entry/entry.html"></iframe> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/entry.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/entry.html new file mode 100644 index 0000000000..82fecfdd38 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/entry.html @@ -0,0 +1,4 @@ +<body onload="top.go()"> +<iframe id="incumbent" src="../incumbent/empty.html"></iframe> +<iframe id="relevant" src="../relevant/empty.html"></iframe> +</body> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/target.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/target.html new file mode 100644 index 0000000000..5ceaeaf07e --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/target.html @@ -0,0 +1,2 @@ +<!DOCTYPE html> +<script src="../resources/target.js"></script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/empty.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/empty.html new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/empty.html diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/target.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/target.html new file mode 100644 index 0000000000..5ceaeaf07e --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/target.html @@ -0,0 +1,2 @@ +<!DOCTYPE html> +<script src="../resources/target.js"></script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/empty.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/empty.html new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/empty.html diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/target.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/target.html new file mode 100644 index 0000000000..5ceaeaf07e --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/target.html @@ -0,0 +1,2 @@ +<!DOCTYPE html> +<script src="../resources/target.js"></script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/context-helper.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/context-helper.js new file mode 100644 index 0000000000..dda338b4cc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/context-helper.js @@ -0,0 +1,34 @@ +// Usage: in the top-level Window, include: +// +// <script src="resources/context-helper.js"></script> +// <script> +// window.scriptToRun = '...'; +// </script> +// <iframe id="entry" src="entry/entry.html"></iframe> +// +// Then `scriptToRun` is evaluated, with: +// - The entry Realm is that of entry/entry.html +// - The incumbent Realm is that of incumbent/empty.html +// - The relevant Realm of `relevantWindow`, `relevantWindow.location` etc. is +// that of relevant/empty.html + +window.scriptToRun = ''; + +const entryUrl = new URL('entry/entry.html', location).href; +const incumbentUrl = new URL('incumbent/empty.html', location).href; +const relevantUrl = new URL('relevant/empty.html', location).href; + +function go() { + const entry = document.querySelector('#entry'); + const incumbent = entry.contentDocument.querySelector('#incumbent'); + const incumbentScript = incumbent.contentDocument.createElement('script'); + incumbentScript.textContent = ` + function go() { + const relevantWindow = + parent.document.querySelector('#relevant').contentWindow; + ${window.scriptToRun} + } + `; + incumbent.contentDocument.head.appendChild(incumbentScript); + incumbent.contentWindow.go(); +} diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/target.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/target.js new file mode 100644 index 0000000000..e3a507b0d8 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/target.js @@ -0,0 +1,11 @@ +window.onload = function() { + let testWindow; + if (opener) { + testWindow = opener.top; + } else { + testWindow = top; + } + testWindow.postMessage( + {location: location.href, referrer: document.referrer}, + "*"); +} diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment-fire-load-event.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment-fire-load-event.html new file mode 100644 index 0000000000..f74bbfd7d3 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment-fire-load-event.html @@ -0,0 +1,31 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<body> +<script> +async_test(t => { + const crossOriginUrl = new URL(get_host_info().HTTPS_REMOTE_ORIGIN); + crossOriginUrl.pathname = "/common/blank.html"; + const i = document.createElement("iframe"); + i.src = crossOriginUrl; + document.body.appendChild(i); + + let wasLoadEventFired = false; + i.onload = t.step_func(() => { + // Though iframe is cross-origin and changing hash leads soft reload, the + // load event should be fired to protect sensitive information. + // See: https://crbug.com/1248444 + crossOriginUrl.hash = "#foo"; + i.onload = () => { + assert_false(wasLoadEventFired) + wasLoadEventFired = true; + // Wait for a while to ensure other onload events are never fired. + t.step_timeout(() => t.done(), 100); + }; + i.src = crossOriginUrl; + }); + +}, "Changing the URL hash of a cross-origin iframe should fire a load event"); +</script> +</body> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment.html new file mode 100644 index 0000000000..ed228ad59b --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment.html @@ -0,0 +1,27 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<body> +<script> +async_test(t => { + let starting_history_length = history.length; + let cross_origin_url = new URL(get_host_info().HTTPS_REMOTE_ORIGIN); + cross_origin_url.pathname = "/common/blank.html"; + cross_origin_url.hash = "#foo"; + let i = document.createElement("iframe"); + i.src = cross_origin_url; + document.body.appendChild(i); + + window.onload = () => t.step_timeout(() => { + assert_equals(starting_history_length, history.length); + i.src = cross_origin_url; + // Give the navigation time to happen - no events will fire. + t.step_timeout(() => { + assert_equals(starting_history_length + 1, history.length); + t.done(); + }, 100); + }, 0); +}, "Navigating a cross-origin iframe to its current url should not replace"); +</script> +</body> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url.html new file mode 100644 index 0000000000..9996d58914 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url.html @@ -0,0 +1,22 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<body> +<script> +async_test(t => { + let starting_history_length = history.length; + let cross_origin_url = new URL(get_host_info().HTTPS_REMOTE_ORIGIN); + cross_origin_url.pathname = "/common/blank.html"; + let i = document.createElement("iframe"); + i.src = cross_origin_url; + document.body.appendChild(i); + + window.onload = () => t.step_timeout(() => { + assert_equals(starting_history_length, history.length); + i.onload = t.step_func_done(() => assert_equals(starting_history_length + 1, history.length)); + i.src = cross_origin_url; + }, 0); +}, "Navigating a cross-origin iframe to its current url should not replace"); +</script> +</body> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-to-unparseable-url.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-to-unparseable-url.html new file mode 100644 index 0000000000..f4e4e36f37 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-to-unparseable-url.html @@ -0,0 +1,58 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>location.href unparseable URL throws a SyntaxError DOMException</title> +<link rel="help" href="https://html.spec.whatwg.org/#the-location-interface:dom-location-href-2"> +<link rel="help" href="https://html.spec.whatwg.org/#following-hyperlinks-2"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<body> +<script> +const kUnparseableURL = self.origin + ":notaport/common/blank.html"; + +promise_test(async t => { + const win = window.open("/common/blank.html"); + t.add_cleanup(() => { + win.close(); + }); + + await new Promise(resolve => { + win.onload = resolve; + }); + + assert_throws_dom("SyntaxError", win.DOMException, () => { + win.location.href = kUnparseableURL; + }, "location.href setter throws a SyntaxError DOMException"); +}, "location.href setter throws a SyntaxError DOMException for unparseable " + + "URLs"); + +promise_test(async t => { + const win = window.open("/common/blank.html"); + t.add_cleanup(() => { + win.close(); + }); + + await new Promise(resolve => { + win.onload = resolve; + }); + + // If the newly-opened window tries to navigate, fail the test. + const failPromise = new Promise((resolve, reject) => { + win.onunload = () => + reject(new Error("Navigation was attempted to unparseable URL")); + }); + + // A promise to wait on to confirm the newly-opened window did not navigate. + const successPromise = new Promise(resolve => { + t.step_timeout(resolve, 2000); + }); + + const a = win.document.createElement('a'); + a.href = kUnparseableURL; + win.document.body.append(a); + a.click(); + + return Promise.race([successPromise, failPromise]); +}, "<a> tag navigate fails for unparseable URLs"); +</script> +</body> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.js new file mode 100644 index 0000000000..f23e2c440b --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.js @@ -0,0 +1,15 @@ +// META: title=Cross-origin navigation started from unload handler must be ignored +// META: script=../resources/helpers.js + +promise_test(async () => { + const iframe = await addIframe(); + + iframe.contentWindow.addEventListener("unload", () => { + iframe.contentWindow.location.href = "//{{hosts[][www]}}/common/blank.html?fail"; + }); + + iframe.src = "/common/blank.html?pass"; + + await waitForIframeLoad(iframe); + assert_equals(iframe.contentWindow.location.search, "?pass"); +}); diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-data-url.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-data-url.window.js new file mode 100644 index 0000000000..cf39b01107 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-data-url.window.js @@ -0,0 +1,15 @@ +// META: title=data: URL navigation started from unload handler must be ignored +// META: script=../resources/helpers.js + +promise_test(async () => { + const iframe = await addIframe(); + + iframe.contentWindow.addEventListener("unload", () => { + iframe.contentWindow.location.href = + `data:text/html,unload<script>parent.postMessage('fail', '*');</script>`; + }); + + iframe.src = + `data:text/html,load<script>parent.postMessage('pass', '*')</script>`; + assert_equals(await waitForMessage(iframe.contentWindow), "pass"); +}); diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-1.html new file mode 100644 index 0000000000..e06def9b20 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-1.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> + +<h1>navigation-unload-form-submit-1.html</h1> + +<script> +let isSubmit = false; + +window.unload = function () { + window.location = 'is-Submit' + isSubmit; +} + +function setIsSubmit() { + isSubmit = true; +} +</script> + +<form onsubmit="setIsSubmit" action="navigation-unload-form-submit-2.html"> + <input type="submit"> +</form> + +<script> +parent.finishedLoading(); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-2.html new file mode 100644 index 0000000000..43cd3c1b33 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-2.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> + +<h1>navigation-unload-form-submit-2.html</h1> + +<script> +parent.finishedLoading(); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit.html new file mode 100644 index 0000000000..029170d46a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#navigating-across-documents"> +<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me"> + +<iframe id="i" src="navigation-unload-form-submit-1.html"></iframe> + +<!-- derived from https://bugzilla.mozilla.org/show_bug.cgi?id=247660#c0 --> + +<script> +var test = async_test('Tests that navigation during an unload caused by a form submit does nothing'); +window.onload = test.step_func(function() { + var i = document.querySelector('#i'); + + window.finishedLoading = test.step_func_done(function () { + assert_equals(i.contentWindow.location.pathname.split('/').pop(), 'navigation-unload-form-submit-2.html'); + assert_equals(i.contentWindow.location.hash, ''); + }); + + i.contentWindow.document.querySelector('input[type="submit"]').click(); +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-javascript-url.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-javascript-url.window.js new file mode 100644 index 0000000000..abbcb888a0 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-javascript-url.window.js @@ -0,0 +1,15 @@ +// META: title=javascript: URL navigation started from unload handler must be ignored +// META: script=../resources/helpers.js + +promise_test(async () => { + const iframe = await addIframe(); + + iframe.contentWindow.addEventListener("unload", () => { + iframe.contentWindow.location.href = + `javascript:"unload<script>parent.postMessage('fail', '*');</script>"`; + }); + + iframe.src = + `javascript:"load<script>parent.postMessage('pass', '*')</script>"`; + assert_equals(await waitForMessage(iframe.contentWindow), "pass"); +}); diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-1.html new file mode 100644 index 0000000000..3b5d725fee --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-1.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> + +<h1>navigation-unload-same-origin-fragment-1.html</h1> + +<script> +if (parent.finishedLoading) { + parent.finishedLoading(); +} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-2.html new file mode 100644 index 0000000000..2fb7f50b43 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-2.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> + +<h1>navigation-unload-same-origin-fragment-2.html</h1> + +<script> +if (parent.finishedLoading) { + parent.finishedLoading(); +} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html new file mode 100644 index 0000000000..c4dceb9e04 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#navigating-across-documents"> +<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me"> + +<iframe id="i" src="navigation-unload-same-origin-fragment-1.html"></iframe> + +<!-- a timeout indicates that setting i.contentWindow.location.hash (a second navigation) aborted the first navigation, + and so it stayed on a.html and finishedLoading was never called --> + +<script> +var test = async_test('Tests that a fragment navigation in the unload handler will not block the initial navigation'); +window.onload = test.step_func(function() { + var i = document.querySelector('#i'); + + i.contentWindow.onunload = test.step_func(function() { + i.contentWindow.location.hash = '#fragment'; + }); + + window.finishedLoading = test.step_func_done(function () { + assert_equals(i.contentWindow.location.pathname.split('/').pop(), 'navigation-unload-same-origin-fragment-2.html'); + assert_equals(i.contentWindow.location.hash, ''); + }); + + i.contentWindow.location.href = 'navigation-unload-same-origin-fragment-2.html'; +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.js new file mode 100644 index 0000000000..826af4b75b --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.js @@ -0,0 +1,15 @@ +// META: title=Same-origin navigation started from unload handler must be ignored +// META: script=../resources/helpers.js + +promise_test(async () => { + const iframe = await addIframe(); + + iframe.contentWindow.addEventListener("unload", () => { + iframe.contentWindow.location.href = "/common/blank.html?fail"; + }); + + iframe.src = "/common/blank.html?pass"; + + await waitForIframeLoad(iframe); + assert_equals(iframe.contentWindow.location.search, "?pass"); +}); diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/plugin-document.historical.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/plugin-document.historical.html new file mode 100644 index 0000000000..547917b795 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/plugin-document.historical.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Same-origin PDFs must not create accessible Document objects</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<!-- https://github.com/whatwg/html/pull/6947 --> + +<iframe src="resources/portable-document-format-sample-valid.pdf"></iframe> + +<script> +setup({ explicit_done: true }); + +window.onload = () => { + test(() => { + assert_throws_dom("SecurityError", () => { + document.querySelector("iframe").contentWindow.document; + }); + }); + done(); +}; +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-about.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-about.window.js new file mode 100644 index 0000000000..5480911895 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-about.window.js @@ -0,0 +1,34 @@ +"use strict"; + +["about:blank", "about:srcdoc", "about:nonstandard"].forEach(aboutURL => { + promise_test(async t => { + const iframe = document.createElement("iframe"); + iframe.src = `resources/redirect.py?location=${aboutURL}`; + document.body.append(iframe); + + // Unfortunately Firefox does not fire a load event for network errors yet, but there is no + // other way I can see to test this. (Also applicable below.) + await new Promise(r => iframe.onload = r); + + // Must throw since error pages are opaque origin. + assert_throws_dom("SecurityError", () => { + iframe.contentWindow.document; + }); + }, `An iframe with src set to a redirect to ${aboutURL}`); + + promise_test(async t => { + const iframe = document.createElement("iframe"); + iframe.src = "/common/blank.html"; + document.body.append(iframe); + + await new Promise(r => iframe.onload = r); + + iframe.contentWindow.location.href = `resources/redirect.py?location=${aboutURL}`; + await new Promise(r => iframe.onload = r); + + // Must throw since error pages are opaque origin. + assert_throws_dom("SecurityError", () => { + iframe.contentWindow.document; + }); + }, `An iframe that is navigated to a redirect to ${aboutURL}`); +}); diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-data.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-data.html new file mode 100644 index 0000000000..f9e8021ddf --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-data.html @@ -0,0 +1,75 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Redirecting to data: URLs is disallowed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<body> +<script> +"use strict"; + +promise_test(async (t) => { + window.onmessage = t.unreached_func("must not be messaged"); + t.add_cleanup(() => { window.onmessage = null; }); + + const iframe = document.createElement("iframe"); + iframe.src = `resources/redirect.py?location=data:text/html,FAIL<script>parent.postMessage('FAIL', '*')</${'script'}>`; + document.body.append(iframe); + + await new Promise(r => iframe.onload = r); + + // Must throw since error pages are opaque origin. + assert_throws_dom("SecurityError", () => { + iframe.contentWindow.document; + }); + + // Test passes if after 100 ms we haven't gotten the message. + await new Promise(r => t.step_timeout(r, 100)); +}, "Loading an iframe with src=redirecting URL"); + +promise_test(async (t) => { + window.onmessage = t.unreached_func("must not be messaged"); + t.add_cleanup(() => { window.onmessage = null; }); + + const iframe = document.createElement("iframe"); + iframe.src = "/common/blank.html"; + document.body.append(iframe); + + await new Promise(r => iframe.onload = r); + + iframe.contentWindow.location.href = `resources/redirect.py?location=data:text/html,FAIL<script>parent.postMessage('FAIL', '*')</${'script'}>`; + await new Promise(r => iframe.onload = r); + + // Must throw since error pages are opaque origin. + assert_throws_dom("SecurityError", () => { + iframe.contentWindow.document; + }); + + // Test passes if after 100 ms we haven't gotten the message. + await new Promise(r => t.step_timeout(r, 100)); +}, "Navigating an iframe to a redirecting URL"); + +promise_test(async (t) => { + window.onmessage = t.unreached_func("must not be messaged"); + t.add_cleanup(() => { window.onmessage = null; }); + + const w = window.open(`resources/redirect.py?location=data:text/html,FAIL<script>parent.postMessage('FAIL', '*')</${'script'}>`); + + // Test passes if after 100 ms we haven't gotten the message. + await new Promise(r => t.step_timeout(r, 100)); +}, "Loading a popup directly to the redirecting URL"); + +promise_test(async (t) => { + const w = window.open(`resources/message-opener.html`); + await new Promise(r => window.onmessage = r); + + window.onmessage = t.unreached_func("must not be messaged"); + t.add_cleanup(() => { window.onmessage = null; }); + + w.location.href = `resources/redirect.py?location=data:text/html,FAIL<script>parent.postMessage('FAIL', '*')</${'script'}>`; + + // Test passes if after 100 ms we haven't gotten the message. + await new Promise(r => t.step_timeout(r, 100)); +}, "Loading a popup that eventually goes to the redirecting URL"); + +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-unparseable-url.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-unparseable-url.html new file mode 100644 index 0000000000..b025f34478 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-unparseable-url.html @@ -0,0 +1,71 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Session history interaction with redirects to unparseable URLs</title> +<link rel="help" href="https://html.spec.whatwg.org/#create-navigation-params-by-fetching"> +<link rel="help" href="https://html.spec.whatwg.org/#read-ua-inline"> +<script src="/common/utils.js"></script> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<body> +<script> +const kUnparseableURL = self.origin + ":notaport/common/blank.html"; + +promise_test(async t => { + const iframe = document.createElement('iframe'); + t.add_cleanup(() => { + iframe.remove(); + }); + + function getIframeLoadPromise() { + return new Promise(resolve => { + iframe.addEventListener('load', () => { + // Wait for the iframe to load + one task so that its navigations are + // not done in "replace" mode. + t.step_timeout(resolve, 0); + }, {once: true}); + }); + } + + document.body.append(iframe); + + assert_equals(history.length, 1, "Precondition: history.length is 1"); + + const first_load_promise = getIframeLoadPromise(); + iframe.src = '/common/blank.html'; + await first_load_promise; + + // This navigation will fail, because it redirects to an unparseable URL. + const error_load_promise = getIframeLoadPromise(); + const error_url = new URL('resources/no-cache-single-redirect.py', location.href); + error_url.searchParams.append('uuid', token()); + error_url.searchParams.append('location', kUnparseableURL); + iframe.src = error_url; + await error_load_promise; + + assert_equals(history.length, 2, + "history.length is 2 after two iframe navigations beyond the initial " + + "about:blank Document, the first of which 'replaced' the initial " + + "about:blank Document"); + + // Per https://html.spec.whatwg.org/#read-ua-inline, error Documents have + // opaque origins, so the `contentDocument` shouldn't be accessible. + assert_equals(iframe.contentDocument, null, + "Cannot reach iframe.contentDocument for error Documents"); + + const back_load_promise = getIframeLoadPromise(); + history.back(); + await back_load_promise; + + const forward_load_promise = getIframeLoadPromise(); + history.forward(); + await forward_load_promise; + + assert_not_equals(iframe.contentDocument, null, "iframe.contentDocument is accessible"); + assert_equals(iframe.contentDocument.body.innerText, "No redirect", + "Traversal to history entry whose URL was once associated with an " + + "error Document correctly requests the same URL again"); +}, "Navigating to a url (A) that redirects to an unparseable URL (B), saves " + + "the URL (A) in the history entry, for later traversal"); +</script> +</body> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/README.md b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/README.md new file mode 100644 index 0000000000..52548db656 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/README.md @@ -0,0 +1 @@ +See `/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/parsing.html` for more detailed parsing tests (shared with `<meta http-equiv=refresh>`). diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/navigate.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/navigate.window.js new file mode 100644 index 0000000000..7d5a0fe21d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/navigate.window.js @@ -0,0 +1,23 @@ +async_test(t => { + const frame = document.createElement("iframe"); + frame.src = "resources/refresh.py" + frame.onload = t.step_func(() => { + // Could be better by verifying that resources/refresh.py loads too + if(frame.contentWindow.location.href === (new URL("resources/refreshed.txt?\u0080\u00FF", self.location)).href) { // Make sure bytes got mapped to code points of the same value + t.done(); + } + }); + document.body.appendChild(frame) +}, "When navigating the Refresh header needs to be followed"); + +async_test(t => { + const frame = document.createElement("iframe"); + frame.src = "resources/multiple.asis" + frame.onload = t.step_func(() => { + // Could be better by verifying that resources/refresh.py loads too + if(frame.contentWindow.location.href === (new URL("resources/refreshed.txt", self.location)).href) { + t.done(); + } + }); + document.body.appendChild(frame) +}, "When there's both a Refresh header and <meta> the Refresh header wins") diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/multiple.asis b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/multiple.asis new file mode 100644 index 0000000000..3026d8297b --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/multiple.asis @@ -0,0 +1,6 @@ +HTTP/1.1 200 OK +Refresh: 0,./refreshed.txt +Content-Type:text/html + +I don't understand. +<meta http-equiv=refresh content=1;./> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh.py b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh.py new file mode 100644 index 0000000000..ecdd24f268 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh.py @@ -0,0 +1,4 @@ +def main(request, response): + response.headers.set(b"Content-Type", b"text/plain") + response.headers.set(b"Refresh", b"0;./refreshed.txt?\x80\xFF") # Test byte to Unicode conversion + response.content = u"Not refreshed.\n" diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refreshed.txt b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refreshed.txt new file mode 100644 index 0000000000..5df065b456 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refreshed.txt @@ -0,0 +1 @@ +Have another. diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/subresource.any.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/subresource.any.js new file mode 100644 index 0000000000..930dd34ad5 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/subresource.any.js @@ -0,0 +1,6 @@ +promise_test(() => { + return fetch("resources/refresh.py").then(response => { + assert_equals(response.headers.get("refresh"), "0;./refreshed.txt?\u0080\u00FF"); // Make sure bytes got mapped to code points of the same value + assert_equals(response.url, (new URL("resources/refresh.py", self.location)).href); + }); +}, "Refresh does not affect subresources."); diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-load.html new file mode 100644 index 0000000000..e035b1f517 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-load.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onload = () => { + const a = document.createElement("a"); + a.href = "/common/blank.html?thereplacement"; + document.body.append(a); + a.click(); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load"); +}, "aElement.click() during the load event must NOT replace"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-pageshow.html new file mode 100644 index 0000000000..006ce531e0 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-pageshow.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onpageshow = () => { + const a = document.createElement("a"); + a.href = "/common/blank.html?thereplacement"; + document.body.append(a); + a.click(); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load"); +}, "aElement.click() during the pageshow event must NOT replace"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html new file mode 100644 index 0000000000..be7d1a9849 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + const a = document.createElement("a"); + a.href = "/common/blank.html?thereplacement"; + document.currentScript.before(a); + a.click(); + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load"); +}, "aElement.click() before the load event must NOT replace"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-load.html new file mode 100644 index 0000000000..811c828331 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-load.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onload = () => { + const a = document.createElement("a"); + a.href = "/common/blank.html?thereplacement"; + a.id = "the-anchor"; + a.textContent = "needs to have content to be clickable"; + document.body.append(a); + parent.test_driver.click(a); + }; + `; + + const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load"); +}, "User click on <a> during the load event must NOT replace"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-pageshow.html new file mode 100644 index 0000000000..6621b081e1 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-pageshow.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onpageshow = () => { + const a = document.createElement("a"); + a.href = "/common/blank.html?thereplacement"; + a.id = "the-anchor"; + a.textContent = "needs to have content to be clickable"; + document.body.append(a); + parent.test_driver.click(a); + }; + `; + + const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load"); +}, "User click on <a> during the pageshow event must NOT replace"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click.html new file mode 100644 index 0000000000..c9034f3573 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + const a = document.createElement("a"); + a.href = "/common/blank.html?thereplacement"; + a.id = "the-anchor"; + a.textContent = "needs to have content to be clickable"; + document.currentScript.before(a); + parent.test_driver.click(a); + `; + + const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load"); +}, "User click on <a> before the load event must NOT replace"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-load.html new file mode 100644 index 0000000000..0dee49edb3 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-load.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onload = () => { + const form = document.createElement("form"); + form.action = "/common/blank.html"; + + const input = document.createElement("input"); + input.type = "hidden"; + input.name = "thereplacement"; + form.append(input); + + document.body.append(form); + form.requestSubmit(); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement="; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace during the load event, triggered by formElement.submit()"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-pageshow.html new file mode 100644 index 0000000000..0cf0838496 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-pageshow.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onpageshow = () => { + const form = document.createElement("form"); + form.action = "/common/blank.html"; + + const input = document.createElement("input"); + input.type = "hidden"; + input.name = "thereplacement"; + form.append(input); + + document.body.append(form); + form.requestSubmit(); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement="; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace during the pageshow event, triggered by formElement.submit()"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html new file mode 100644 index 0000000000..80beb3718b --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + const form = document.createElement("form"); + form.action = "/common/blank.html"; + + const input = document.createElement("input"); + input.type = "hidden"; + input.name = "thereplacement"; + form.append(input); + + document.currentScript.before(form); + form.requestSubmit(); + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement="; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace before load, triggered by formElement.requestSubmit()"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-load.html new file mode 100644 index 0000000000..1b3a163176 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-load.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onload = () => { + const form = document.createElement("form"); + form.action = "/common/blank.html"; + + const input = document.createElement("input"); + input.type = "hidden"; + input.name = "thereplacement"; + form.append(input); + + const button = document.createElement("button"); + button.type = "submit"; + form.append(button); + + document.body.append(form); + button.click(); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement="; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace during load, triggered by submitButton.click()"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-pageshow.html new file mode 100644 index 0000000000..c0022fd0f2 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-pageshow.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onpageshow = () => { + const form = document.createElement("form"); + form.action = "/common/blank.html"; + + const input = document.createElement("input"); + input.type = "hidden"; + input.name = "thereplacement"; + form.append(input); + + const button = document.createElement("button"); + button.type = "submit"; + form.append(button); + + document.body.append(form); + button.click(); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement="; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace during pageshow, triggered by submitButton.click()"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html new file mode 100644 index 0000000000..873c49b5be --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + const form = document.createElement("form"); + form.action = "/common/blank.html"; + + const input = document.createElement("input"); + input.type = "hidden"; + input.name = "thereplacement"; + form.append(input); + + const button = document.createElement("button"); + button.type = "submit"; + form.append(button); + + document.currentScript.before(form); + button.click(); + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement="; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace before load, triggered by submitButton.click()"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame-crossorigin.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame-crossorigin.sub.html new file mode 100644 index 0000000000..a2ea20bf7f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame-crossorigin.sub.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<form target="the-frame"> + <input type="hidden" name="pushed"> +</form> + +<script> +"use strict"; +promise_test(async t => { + const startingHistoryLength = history.length; + + const form = document.querySelector("form"); + const frameEndingURL = changeURLHost( + absoluteURL("resources/slow-message-source-with-history-and-location.html?pushed="), + "{{hosts[][www]}}" + ); + form.action = frameEndingURL; + + const frameStartingCode = ` + parent.postMessage({ historyLength: history.length, locationHref: location.href }, "*"); + `; + + const frameStartingURL = codeInjectorURL(frameStartingCode); + const frame = insertIframe(t, frameStartingURL, "the-frame"); + t.add_cleanup(() => frame.remove()); // helps avoid waiting for the slow load to finish the tests + assert_equals(history.length, startingHistoryLength, "Inserting frame must not change history.length"); + + const frameBeforeLoadedMessage = await waitForMessage(); + assert_equals(frameBeforeLoadedMessage.historyLength, startingHistoryLength, "frame's starting history.length"); + assert_equals(frameBeforeLoadedMessage.locationHref, frame.src, "frame's starting location.href"); + + form.submit(); + + const frameAfterFormSubmitMessage = await waitForMessage(); + assert_equals(frameAfterFormSubmitMessage.historyLength, startingHistoryLength + 1, "frame's after-submit history.length"); + assert_equals(frameAfterFormSubmitMessage.locationHref, frameEndingURL, "frame's after-submit location.href"); +}, "No replace before load, triggered by cross-iframe formElement.submit() [iframe is cross-origin]"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame.html new file mode 100644 index 0000000000..d647e6ad06 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<form target="the-frame"> + <input type="hidden" name="pushed"> +</form> + +<script> +"use strict"; +promise_test(async t => { + const startingHistoryLength = history.length; + + const form = document.querySelector("form"); + const frameEndingURL = absoluteURL("resources/slow-message-source-with-history-and-location.html?pushed="); + form.action = frameEndingURL; + + const frameStartingCode = ` + window.onload = () => { window.onloadFired = true; }; + parent.postMessage({ historyLength: history.length, locationHref: location.href }, "*"); + parent.document.querySelector("form").submit(); + `; + + const frameStartingURL = codeInjectorURL(frameStartingCode); + const frame = insertIframe(t, frameStartingURL, "the-frame"); + t.add_cleanup(() => frame.remove()); // helps avoid waiting for the slow load to finish the tests + assert_equals(history.length, startingHistoryLength, "Inserting frame must not change history.length"); + + const frameBeforeLoadedMessage = await waitForMessage(); + assert_equals(frameBeforeLoadedMessage.historyLength, startingHistoryLength, "frame's starting history.length"); + assert_equals(frameBeforeLoadedMessage.locationHref, frame.src, "frame's starting location.href"); + assert_equals(frame.contentWindow.onloadFired, undefined, "frame's onload not fired yet"); + + const frameAfterFormSubmitMessage = await waitForMessage(); + assert_equals(frameAfterFormSubmitMessage.historyLength, startingHistoryLength + 1, "frame's after-submit history.length"); + assert_equals(frameAfterFormSubmitMessage.locationHref, frameEndingURL, "frame's after-submit location.href"); +}, "No replace before load, triggered by cross-iframe formElement.submit()"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-load.html new file mode 100644 index 0000000000..2a60f44fc6 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-load.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onload = () => { + const form = document.createElement("form"); + form.action = "/common/blank.html"; + + const input = document.createElement("input"); + input.type = "hidden"; + input.name = "thereplacement"; + form.append(input); + + document.body.append(form); + form.submit(); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement="; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace during the load event, triggered by formElement.submit()"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-pageshow.html new file mode 100644 index 0000000000..886bf469a2 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-pageshow.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onpageshow = () => { + const form = document.createElement("form"); + form.action = "/common/blank.html"; + + const input = document.createElement("input"); + input.type = "hidden"; + input.name = "thereplacement"; + form.append(input); + + document.body.append(form); + form.submit(); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement="; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace during the pageshow event, triggered by formElement.submit()"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup-crossorigin.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup-crossorigin.sub.html new file mode 100644 index 0000000000..d54cc81477 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup-crossorigin.sub.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<form target="the-window"> + <input type="hidden" name="pushed"> +</form> + +<script> +"use strict"; +promise_test(async t => { + const form = document.querySelector("form"); + const wEndingURL = changeURLHost( + absoluteURL("resources/slow-message-source-with-history-and-location.html?pushed="), + "{{hosts[][www]}}" + ); + form.action = wEndingURL; + + const wStartingCode = ` + opener.postMessage({ historyLength: history.length, locationHref: location.href }, "*"); + `; + + const wStartingURL = codeInjectorURL(wStartingCode); + const w = window.open(wStartingURL, "the-window"); + t.add_cleanup(() => w.close()); + + const wBeforeLoadedMessage = await waitForMessage(); + assert_equals(wBeforeLoadedMessage.historyLength, 1, "window's starting history.length"); + assert_equals(wBeforeLoadedMessage.locationHref, wStartingURL, "window's starting location.href"); + + form.submit(); + + const wAfterFormSubmitMessage = await waitForMessage(); + assert_equals(wAfterFormSubmitMessage.historyLength, 2, "window's after-submit history.length"); + assert_equals(wAfterFormSubmitMessage.locationHref, wEndingURL, "window's after-submit location.href"); +}, "No replace before load, triggered by formElement.submit() in the opener window, after the opener has loaded [window is cross-origin]"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup.html new file mode 100644 index 0000000000..b27d54675d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<form target="the-window"> + <input type="hidden" name="pushed"> +</form> + +<script> +"use strict"; +promise_test(async t => { + const form = document.querySelector("form"); + const wEndingURL = absoluteURL("resources/slow-message-source-with-history-and-location.html?pushed="); + form.action = wEndingURL; + + const wStartingCode = ` + window.onload = () => { window.onloadFired = true; }; + opener.postMessage({ historyLength: history.length, locationHref: location.href }, "*"); + opener.document.querySelector("form").submit(); + `; + + const wStartingURL = codeInjectorURL(wStartingCode); + const w = window.open(wStartingURL, "the-window"); + t.add_cleanup(() => w.close()); + + const wBeforeLoadedMessage = await waitForMessage(); + assert_equals(wBeforeLoadedMessage.historyLength, 1, "window's starting history.length"); + assert_equals(wBeforeLoadedMessage.locationHref, wStartingURL, "window's starting location.href"); + assert_equals(w.onloadFired, undefined, "window's onload not fired yet"); + + const wAfterFormSubmitMessage = await waitForMessage(); + assert_equals(wAfterFormSubmitMessage.historyLength, 2, "window's after-submit history.length"); + assert_equals(wAfterFormSubmitMessage.locationHref, wEndingURL, "window's after-submit location.href"); +}, "No replace before load, triggered by formElement.submit() in the opener window, after the opener has loaded"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit.html new file mode 100644 index 0000000000..eace0b0a69 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + const form = document.createElement("form"); + form.action = "/common/blank.html"; + + const input = document.createElement("input"); + input.type = "hidden"; + input.name = "thereplacement"; + form.append(input); + + document.currentScript.before(form); + form.submit(); + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement="; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace before load, triggered by same-document formElement.submit()"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-load.html new file mode 100644 index 0000000000..233d80d8d7 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-load.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onload = () => { + history.pushState(null, null, "/common/blank.html?thereplacement"); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the replacement"); +}, "history.pushState() during the load event must NOT replace"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-pageshow.html new file mode 100644 index 0000000000..a4e83baf1d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-pageshow.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onpageshow = () => { + history.pushState(null, null, "/common/blank.html?thereplacement"); + parent.postMessage("done", "*"); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + assert_equals(await waitForMessage(), "done"); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the replacement"); +}, "history.pushState() during the pageshow event must NOT replace"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate.html new file mode 100644 index 0000000000..2faef7c319 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("history.pushState(null, null, `/common/blank.html?thereplacement`);"); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the replacement"); +}, "history.pushState() before the load event must NOT replace"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-load.html new file mode 100644 index 0000000000..83f26b2aa9 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-load.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onload = () => { + parent.document.querySelectorAll("iframe")[1].src = "/common/blank.html?thereplacement"; + }; + `; + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace during the load event, triggered by setting iframeElement.src"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-pageshow.html new file mode 100644 index 0000000000..61345cf7f9 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-pageshow.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onpageshow = () => { + parent.document.querySelectorAll("iframe")[1].src = "/common/blank.html?thereplacement"; + }; + `; + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace during the pageshow event, triggered by setting iframeElement.src"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src.html new file mode 100644 index 0000000000..47407c106e --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onload = () => { window.onloadFired = true; }; + `; + const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + const absoluteStartURL = (new URL(startURL, location.href)).href; + while (true) { + if (iframe.contentWindow.location.href === absoluteStartURL) { + break; + } + await new Promise(r => setTimeout(r, 0)); + } + + assert_equals(iframe.contentWindow.location.href, (new URL(startURL, location.href)).href, "Iframe must be navigated away from the initial about:blank document"); + assert_equals(iframe.contentWindow.onloadFired, undefined, "onload must not yet have fired"); + + iframe.src = afterReplacementURL; + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace before load, triggered by setting iframeElement.src"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-load.html new file mode 100644 index 0000000000..2b4ac49533 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-load.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onload = () => { + location.assign("/common/blank.html?thereplacement"); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace during the load event, triggered by location.assign()"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-pageshow.html new file mode 100644 index 0000000000..0f84cbe246 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-pageshow.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onpageshow = () => { + location.assign("/common/blank.html?thereplacement"); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace during the pageshow event, triggered by location.assign()"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-user-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-user-click.html new file mode 100644 index 0000000000..785ec024e0 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-user-click.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + const button = document.createElement("button"); + button.id = "the-button"; + button.textContent = "needs to have content to be clickable"; + button.onclick = () => { location.assign("/common/blank.html?thereplacement"); }; + document.currentScript.before(button); + parent.test_driver.click(button); + `; + + const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load"); +}, "NO replace before load, triggered by location.assign()"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign.html new file mode 100644 index 0000000000..53ea96ffea --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("location.assign(`/common/blank.html?thereplacement`);"); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "Replace before load, triggered by location.assign()"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-load.html new file mode 100644 index 0000000000..e4c8ca80a1 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-load.html @@ -0,0 +1,76 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Replace during the load event, triggered by location setters</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onload = () => { + location.href = "/common/blank.html?thereplacement"; + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "href"); + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onload = () => { + location.search = "thereplacement"; + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "resources/code-injector.html?thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); +}, "search"); + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onload = () => { + location.hash = "thereplacement"; + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = startURL + "#thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "hash"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-pageshow.html new file mode 100644 index 0000000000..9997783ff5 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-pageshow.html @@ -0,0 +1,77 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Replace during the pageshow event, triggered by location setters</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onpageshow = () => { + location.href = "/common/blank.html?thereplacement"; + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "href"); + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onpageshow = () => { + location.search = "thereplacement"; + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "resources/code-injector.html?thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); +}, "search"); + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onpageshow = () => { + location.hash = "thereplacement"; + parent.postMessage("done", "*"); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = startURL + "#thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + assert_equals(await waitForMessage(), "done"); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "hash"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-click.html new file mode 100644 index 0000000000..9276327f1a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-click.html @@ -0,0 +1,82 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>No replace before load, triggered by location setters called as part of user-initiated clicks</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<body> +<script> +"use strict"; + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + const button = document.createElement("button"); + button.id = "the-button"; + button.textContent = "needs to have content to be clickable"; + button.onclick = () => { location.href = "/common/blank.html?thereplacement"; }; + document.currentScript.before(button); + parent.test_driver.click(button); + `; + + const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load"); +}, "href"); + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + const button = document.createElement("button"); + button.id = "the-button"; + button.textContent = "needs to have content to be clickable"; + button.onclick = () => { location.search = "thereplacement"; }; + document.currentScript.before(button); + parent.test_driver.click(button); + `; + + const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "resources/slow-code-injector.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load"); +}, "search"); + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + const button = document.createElement("button"); + button.id = "the-button"; + button.textContent = "needs to have content to be clickable"; + button.onclick = () => { location.hash = "thereplacement"; }; + document.currentScript.before(button); + parent.test_driver.click(button); + `; + + const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = startURL + "#thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load"); +}, "hash"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-mouseup.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-mouseup.html new file mode 100644 index 0000000000..9068e5a116 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-mouseup.html @@ -0,0 +1,89 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>No replace before load, triggered by location setters called as part of user-initiated mouseups</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<!-- + We test this separate from click because the spec as of + https://html.spec.whatwg.org/commit-snapshots/4ba46b025ec806ded7b4911bf8f9dd7bf9ff365e/#location-object-setter-navigate + referenced click handlers specifically, instead of using the general user activation concept which + includes other events like mouseup. +--> + +<body> +<script> +"use strict"; + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + const button = document.createElement("button"); + button.id = "the-button"; + button.textContent = "needs to have content to be clickable"; + button.onmouseup = () => { location.href = "/common/blank.html?thereplacement"; }; + document.currentScript.before(button); + parent.test_driver.click(button); + `; + + const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load"); +}, "href"); + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + const button = document.createElement("button"); + button.id = "the-button"; + button.textContent = "needs to have content to be clickable"; + button.onmouseup = () => { location.search = "thereplacement"; }; + document.currentScript.before(button); + parent.test_driver.click(button); + `; + + const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "resources/slow-code-injector.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load"); +}, "search"); + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + const button = document.createElement("button"); + button.id = "the-button"; + button.textContent = "needs to have content to be clickable"; + button.onmouseup = () => { location.hash = "thereplacement"; }; + document.currentScript.before(button); + parent.test_driver.click(button); + `; + + const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = startURL + "#thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load"); +}, "hash"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter.html new file mode 100644 index 0000000000..b8049f084b --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter.html @@ -0,0 +1,58 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Replace before load, triggered by location setters</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("location.href = `/common/blank.html?thereplacement`;"); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "href"); + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("location.search = `thereplacement`;"); + const afterReplacementURL = "resources/code-injector.html?thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); +}, "search"); + +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("location.hash = `thereplacement`;"); + const afterReplacementURL = startURL + "#thereplacement"; + const iframe = insertIframe(t, startURL); + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement"); + + await checkSentinelIframe(t, sentinelIframe); + assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe"); +}, "hash"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html new file mode 100644 index 0000000000..73dacf0d76 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Subframe</title> + +<body> +<script> +"use strict"; +{{GET[code]}} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/helpers.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/helpers.js new file mode 100644 index 0000000000..58102aa925 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/helpers.js @@ -0,0 +1,89 @@ +window.waitForLoad = (t, iframe, urlRelativeToThisDocument) => { + return new Promise(resolve => { + iframe.addEventListener("load", t.step_func(() => { + assert_equals(iframe.contentWindow.location.href, (new URL(urlRelativeToThisDocument, location.href)).href); + + // Wait a bit longer to ensure all history stuff has settled, e.g. the document is "completely loaded" + // (which happens from a queued task). + setTimeout(resolve, 0); + }), { once: true }); + }); +}; + +window.waitForLoadAllowingIntermediateLoads = (t, iframe, urlRelativeToThisDocument) => { + return new Promise(resolve => { + const handler = t.step_func(() => { + if (iframe.contentWindow.location.href === (new URL(urlRelativeToThisDocument, location.href)).href) { + // Wait a bit longer to ensure all history stuff has settled, e.g. the document is "completely loaded" + // (which happens from a queued task). + setTimeout(resolve, 0); + iframe.removeEventListener("load", handler); + } + }); + + iframe.addEventListener("load", handler); + }); +}; + +window.waitForMessage = () => { + return new Promise(resolve => { + window.addEventListener("message", e => { + resolve(e.data); + }, { once: true }); + }); +}; + +window.setupSentinelIframe = async (t) => { + // If this iframe gets navigated by history.back(), then the iframe under test did not, so we did a replace. + const sentinelIframe = document.createElement("iframe"); + sentinelIframe.src = "/common/blank.html?sentinelstart"; + document.body.append(sentinelIframe); + t.add_cleanup(() => sentinelIframe.remove()); + + await waitForLoad(t, sentinelIframe, "/common/blank.html?sentinelstart"); + + sentinelIframe.src = "/common/blank.html?sentinelend"; + await waitForLoad(t, sentinelIframe, "/common/blank.html?sentinelend"); + + return sentinelIframe; +}; + +window.checkSentinelIframe = async (t, sentinelIframe) => { + // Go back. Since iframe should have done a replace, this should move sentinelIframe back, not iframe. + history.back(); + await waitForLoad(t, sentinelIframe, "/common/blank.html?sentinelstart"); +}; + +window.insertIframe = (t, url, name) => { + const iframe = document.createElement("iframe"); + iframe.src = url; + + // In at least Chromium, window name targeting for form submission doesn't work if the name is set + // after the iframe is inserted into the DOM. So we can't just have callers do this themselves. + if (name) { + iframe.name = name; + } + + document.body.append(iframe); + + // Intentionally not including the following: + // t.add_cleanup(() => iframe.remove()); + // Doing so breaks some of the testdriver.js tests with "cannot find window" errors. + return iframe; +}; + +// TODO(domenic): clean up other tests in the parent directory to use this. +window.absoluteURL = relativeURL => { + return (new URL(relativeURL, location.href)).href; +}; + +// TODO(domenic): clean up other tests in the parent directory to use this. +window.codeInjectorURL = code => { + return absoluteURL("resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code)); +}; + +window.changeURLHost = (url, newHost) => { + const urlObj = new URL(url); + urlObj.host = newHost; + return urlObj.href; +}; diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/message-opener.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/message-opener.html new file mode 100644 index 0000000000..b6f9d31358 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/message-opener.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Popup</title> + +<script> +"use strict"; +opener.postMessage("ready", "*"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-code-injector.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-code-injector.html new file mode 100644 index 0000000000..b7e99dcbfa --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-code-injector.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Subframe</title> + +<body> +<script> +"use strict"; +{{GET[code]}} +</script> + +<!-- + This is necessary in cases involving user interaction because those happen async through the + webdriver infrastructure. Without this, the load event might happen before the click ever goes + through. +--> +<img src="/common/slow.py"> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-message-source-with-history-and-location.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-message-source-with-history-and-location.html new file mode 100644 index 0000000000..da279ebf82 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-message-source-with-history-and-location.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Subframe or popup</title> + +<script> +"use strict"; +(window.opener || window.parent).postMessage( + { historyLength: history.length, locationHref: location.href }, + "*" +); +</script> + +<!-- + This delays the load event, hopefully long enough that we can do whatever before-load action we're aiming for. +--> +<img src="/common/slow.py"> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-load.html new file mode 100644 index 0000000000..5308c01c39 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-load.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + // Generating a new one instead of hard-coding makes running tests manually a bit easier. + const windowName = token(); + + const code = ` + window.onload = () => opener.navigateMe(); + opener.postMessage("arrived at start URL", "*"); + `; + + const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const absoluteStartURL = (new URL(startURL, location.href)).href; + + const afterReplacementURL = "resources/message-opener.html"; + const absoluteAfterReplacementURL = (new URL(afterReplacementURL, location.href)).href; + + window.navigateMe = () => { + window.open(absoluteAfterReplacementURL, windowName); + }; + + // First message sent is ignored; we only check it after navigating back. + const w = window.open(startURL, windowName); + t.add_cleanup(() => w.close()); + + // Wait to get past any initial about:blank + while (true) { + if (w.location.href === absoluteStartURL) { + break; + } + await new Promise(r => t.step_timeout(r, 0)); + } + + assert_equals(w.onloadFired, undefined, "onload must not yet have fired"); + assert_equals(w.history.length, 1, "history.length for the opened window must start at 1"); + + await new Promise(r => { + window.addEventListener("message", t.step_func(e => { + if (e.data === "ready") { + resolve(); + } + })); + }); + + assert_equals(w.history.length, 2, "history.length must increase"); + assert_equals(w.location.href, absoluteAfterReplacementURL); + + const promise = new Promise(resolve => { + window.addEventListener("message", t.step_func(e => { + assert_equals(e.data, "arrived at start URL"); + resolve(); + })); + }); + + w.history.back(); + + await promise; + assert_equals(w.location.href, absoluteStartURL, "1 second after attempting to go back, it indeed went back"); +}, "No replace before load, triggered by window.open() on a non-_self window"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-pageshow.html new file mode 100644 index 0000000000..065d933225 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-pageshow.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + // Generating a new one instead of hard-coding makes running tests manually a bit easier. + const windowName = token(); + + const code = ` + window.onpageshow = () => opener.navigateMe(); + opener.postMessage("arrived at start URL", "*"); + `; + + const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const absoluteStartURL = (new URL(startURL, location.href)).href; + + const afterReplacementURL = "resources/message-opener.html"; + const absoluteAfterReplacementURL = (new URL(afterReplacementURL, location.href)).href; + + window.navigateMe = () => { + window.open(absoluteAfterReplacementURL, windowName); + }; + + // First message sent is ignored; we only check it after navigating back. + const w = window.open(startURL, windowName); + t.add_cleanup(() => w.close()); + + // Wait to get past any initial about:blank + while (true) { + if (w.location.href === absoluteStartURL) { + break; + } + await new Promise(r => t.step_timeout(r, 0)); + } + + assert_equals(w.onloadFired, undefined, "onload must not yet have fired"); + assert_equals(w.history.length, 1, "history.length for the opened window must start at 1"); + + await new Promise(r => { + window.addEventListener("message", t.step_func(e => { + if (e.data === "ready") { + resolve(); + } + })); + }); + + assert_equals(w.history.length, 2, "history.length must increase"); + assert_equals(w.location.href, absoluteAfterReplacementURL); + + const promise = new Promise(resolve => { + window.addEventListener("message", t.step_func(e => { + assert_equals(e.data, "arrived at start URL"); + resolve(); + })); + }); + + w.history.back(); + + await promise; + assert_equals(w.location.href, absoluteStartURL, "1 second after attempting to go back, it indeed went back"); +}, "No replace before load, triggered by window.open() on a non-_self window"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup.html new file mode 100644 index 0000000000..7b3e05f8f6 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup.html @@ -0,0 +1,49 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + // Generating a new one instead of hard-coding makes running manual tests a bit easier. + const windowName = token(); + + const code = ` + window.onload = () => { window.onloadFired = true; }; + `; + + const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const absoluteStartURL = (new URL(startURL, location.href)).href; + + const afterReplacementURL = "resources/message-opener.html"; + const absoluteAfterReplacementURL = (new URL(afterReplacementURL, location.href)).href; + + const w = window.open(startURL, windowName); + t.add_cleanup(() => w.close()); + + // Wait to get past any initial about:blank + while (true) { + if (w.location.href === absoluteStartURL) { + break; + } + await new Promise(r => setTimeout(r, 0)); + } + + assert_equals(w.onloadFired, undefined, "onload must not yet have fired"); + assert_equals(w.history.length, 1, "history.length for the opened window must start at 1"); + + window.open(afterReplacementURL, windowName); + await new Promise(r => { window.onmessage = r; }); + + assert_equals(w.history.length, 2, "history.length must increase"); + assert_equals(w.location.href, absoluteAfterReplacementURL); + + w.history.back(); + + await new Promise(r => t.step_timeout(r, 1000)); + assert_equals(w.location.href, absoluteStartURL, "1 second after attempting to go back, it indeed went back"); +}, "No replace before load, triggered by window.open() on a non-_self window"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-load.html new file mode 100644 index 0000000000..255601ba8d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-load.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onload = () => { + window.open("/common/blank.html?thereplacement", "_self"); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must increase"); +}, "No replace during load, triggered by window.open(_self) on an iframe"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-pageshow.html new file mode 100644 index 0000000000..a8327cfac3 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-pageshow.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const code = ` + window.onpageshow = () => { + window.open("/common/blank.html?thereplacement", "_self"); + }; + `; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must increase"); +}, "No replace during pageshow, triggered by window.open(_self) on an iframe"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self.html new file mode 100644 index 0000000000..10e8f38001 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +"use strict"; +promise_test(async t => { + const sentinelIframe = await setupSentinelIframe(t); + const startingHistoryLength = history.length; + + const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("window.open(`/common/blank.html?thereplacement`, `_self`);"); + const afterReplacementURL = "/common/blank.html?thereplacement"; + const iframe = insertIframe(t, startURL); + + assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length"); + + await waitForLoad(t, iframe, afterReplacementURL); + assert_equals(history.length, startingHistoryLength + 1, "history.length must increase"); +}, "No replace before load, triggered by window.open(_self) on an iframe"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/blank.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/blank.html new file mode 100644 index 0000000000..c50eddd41f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/blank.html @@ -0,0 +1 @@ +<!doctype html> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-cross-origin-inner.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-cross-origin-inner.html new file mode 100644 index 0000000000..72b92c8061 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-cross-origin-inner.html @@ -0,0 +1,20 @@ +<!doctype html> +<script> +const params = new URL(window.location).searchParams; +const property = params.get("property"); + +try { + if (property === null) { + parent.location = "foo"; + } else if (property === "reload") { + parent.location.reload(); + } else if (property === "replace") { + parent.location.replace("foo"); + } else { + parent.location[property] = "foo"; + } + parent.parent.postMessage("success", "*"); +} catch (e) { + parent.parent.postMessage(`error: ${e.name}`, "*"); +} +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-destination.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-destination.html new file mode 100644 index 0000000000..bb8ba4e698 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-destination.html @@ -0,0 +1,4 @@ +<!doctype html> +<script> +parent.postMessage("destination", "*"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-initial.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-initial.html new file mode 100644 index 0000000000..a4a1713a27 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-initial.html @@ -0,0 +1,3 @@ +<!doctype html> +<script>parent.postMessage("initial", "*")</script> +<iframe src="child-navigates-parent-location-inner.html"></iframe> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-inner.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-inner.html new file mode 100644 index 0000000000..4d7d24730f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-inner.html @@ -0,0 +1,5 @@ +<!doctype html> +<script> +parent.parent.postMessage("inner", "*"); +parent.location = "child-navigates-parent-destination.html" +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-initial.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-initial.html new file mode 100644 index 0000000000..943708e0cd --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-initial.html @@ -0,0 +1,3 @@ +<!doctype html> +<script>parent.postMessage("initial", "*")</script> +<iframe src="child-navigates-parent-submit-inner.html"></iframe> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-inner.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-inner.html new file mode 100644 index 0000000000..0b49e13d65 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-inner.html @@ -0,0 +1,6 @@ +<!doctype html> +<form action="child-navigates-parent-destination.html" target="_parent"></form> +<script> +parent.parent.postMessage("inner", "*"); +document.forms[0].submit(); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/click.html new file mode 100644 index 0000000000..8cb03b74d5 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/click.html @@ -0,0 +1,4 @@ +<!doctype html> +<script> +parent.postMessage("click", "*"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/document-domain-set-to-site.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/document-domain-set-to-site.sub.html new file mode 100644 index 0000000000..3c4355c452 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/document-domain-set-to-site.sub.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<script> +"use strict"; +document.domain = "{{host}}"; +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/form.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/form.html new file mode 100644 index 0000000000..6523a82b39 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/form.html @@ -0,0 +1,5 @@ +<!DOCTYPE html> +<script> +parent.postMessage("form navigation", "*"); +</script> +form navigation diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/href.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/href.html new file mode 100644 index 0000000000..eccadadf41 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/href.html @@ -0,0 +1,5 @@ +<!doctype html> +<script> +parent.postMessage("href", "*"); +</script> +href diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-opener.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-opener.html new file mode 100644 index 0000000000..b6f9d31358 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-opener.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Popup</title> + +<script> +"use strict"; +opener.postMessage("ready", "*"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-parent.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-parent.html new file mode 100644 index 0000000000..3656358f2d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-parent.html @@ -0,0 +1,3 @@ +<script> + window.parent.postMessage("ready", "*"); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-1.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-1.sub.html new file mode 100644 index 0000000000..a87b2fd2be --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-1.sub.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Multi-globals test outer subframe</title> + +<script> +"use strict"; +document.domain = "{{hosts[][]}}"; +</script> + +<iframe src="http://{{hosts[][]}}:{{ports[http][0]}}/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-2.sub.html"></iframe> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-2.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-2.sub.html new file mode 100644 index 0000000000..593c428a67 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-2.sub.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Multi-globals test inner subframe</title> + +<script> +"use strict"; +document.domain = "{{hosts[][]}}"; +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/no-cache-single-redirect.py b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/no-cache-single-redirect.py new file mode 100644 index 0000000000..9d3aff817d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/no-cache-single-redirect.py @@ -0,0 +1,21 @@ +# This handler receives requests identified by UUIDs that have mandatory +# `location` query parameters. Every other request for the same URL will result +# in a redirect to the URL described by `location`. When we don't redirect, we +# simply return the HTML document "No redirect". +def main(request, response): + response.headers.set(b"Cache-Control", b"no-store") + + uuid = request.GET.first(b"uuid") + value = request.server.stash.take(uuid) + + if value is None: + response.status = 302 + location = request.GET.first(b"location") + response.headers.set(b"Location", location) + # Ensure that the next time this uuid is request, we don't redirect. + request.server.stash.put(uuid, "sentinel value") + else: + # If we're in this branch, then `value` is not none, but the stash now + # has `None` associated with `uuid`, which means on the next request for + # this `uuid` we'll end up in the above branch instead. + return ([(b"Content-Type", b"text/html")], b"No redirect") diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-that-post-message-to-opener.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-that-post-message-to-opener.html new file mode 100644 index 0000000000..65a0c82149 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-that-post-message-to-opener.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Page that postMessage to its opener</title> +<script> + opener.postMessage('Allowed', '*'); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-with-top-navigating-iframe.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-with-top-navigating-iframe.html new file mode 100644 index 0000000000..568e44296f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-with-top-navigating-iframe.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src=/common/get-host-info.sub.js></script> +<script src=/resources/testdriver.js></script> +<script src=/resources/testdriver-vendor.js></script> +<title>Page that embeds an iframe that navigates its top</title> +<script> +function addIframe() { + const iframe = document.createElement('iframe'); + const path = new URL("top-navigating-page.html", window.location).pathname; + iframe.src = get_host_info().HTTP_NOTSAMESITE_ORIGIN + path; + document.body.appendChild(iframe); +} + +addEventListener('load', () => { + const urlParams = new URLSearchParams(location.search); + const parentUserGesture = urlParams.get('parent_user_gesture') === 'true'; + if (parentUserGesture) + test_driver.bless("Giving parent frame user activation").then(addIframe); + else + addIframe(); +}); +</script> + diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf Binary files differnew file mode 100644 index 0000000000..d008b1f23a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf.headers b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf.headers new file mode 100644 index 0000000000..5a8e57e482 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf.headers @@ -0,0 +1 @@ +Content-Type: application/pdf diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/redirect.py b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/redirect.py new file mode 100644 index 0000000000..3c78c256b0 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/redirect.py @@ -0,0 +1,4 @@ +def main(request, response): + location = request.GET.first(b"location") + response.status = 302 + response.headers.set(b"Location", location) diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/top-navigating-page.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/top-navigating-page.html new file mode 100644 index 0000000000..557f408fba --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/top-navigating-page.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Page that navigates its top</title> +<script src=/common/get-host-info.sub.js></script> +<script> + +let path = new URL("page-that-post-message-to-opener.html", window.location).pathname; +let fullUrl = get_host_info().HTTP_NOTSAMESITE_ORIGIN + path; +try { + top.location = fullUrl; +} catch { + top.opener.postMessage('Denied', '*'); +} + +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/wait-for-messages.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/wait-for-messages.js new file mode 100644 index 0000000000..62ddec49f1 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/wait-for-messages.js @@ -0,0 +1,15 @@ +// Asynchronous function that waits for the given number of messages to be +// received by `window`, then returns those messages. +function waitForMessages(numMessages) { + return new Promise((resolve) => { + const messages = []; + + window.addEventListener("message", function handler(evt) { + messages.push(evt.data); + if (messages.length == numMessages) { + window.removeEventListener("message", handler); + resolve(messages); + } + }); + }); +} diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/xhtml-and-non-utf-8.xhtml b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/xhtml-and-non-utf-8.xhtml new file mode 100644 index 0000000000..3aacf33f1c --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/xhtml-and-non-utf-8.xhtml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="windows-1250"?> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <meta charset="windows-1250"/> + <title>A test document used when you need something very non-default</title> +</head> +<body> +</body> +</html> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent-then-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent-then-fragment.html new file mode 100644 index 0000000000..d01ef4c77a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent-then-fragment.html @@ -0,0 +1,36 @@ +<!doctype html> +<meta charset=utf-8> +<title> + Set location from a parent, then do a fragment navigation from within the + frame. +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe></iframe> +<script> + promise_test(async test => { + // Wait for the DOM to be ready before inserting an <iframe> into it. + await new Promise(resolve => { onload = resolve }); + // Insert an <iframe> and wait for a dummy document to be loaded into it. + let iframe = document.createElement("iframe"); + iframe.src = "support/dummy.html"; + let iframe_loaded = new Promise(resolve => { iframe.onload = resolve }); + document.body.appendChild(iframe); + await iframe_loaded; + // The referrer is the main frame's URL since it initiated the iframe + // creation. + assert_equals(iframe.contentDocument.referrer, document.URL); + // Do a fragment navigation from the frame, which will fire the + // 'hashchange' function. + let hash_changed = new Promise(resolve => { + iframe.contentWindow.onhashchange = resolve + }); + let navigateScript = iframe.contentDocument.createElement("script"); + navigateScript.innerHTML = "location.href = '#foo'"; + iframe.contentDocument.body.appendChild(navigateScript); + await hash_changed; + // The referrer stays the same, even when the last navigation was + // initiated by the iframe (instead of the main frame document). + assert_equals(iframe.contentDocument.referrer, document.URL); + }); +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent.html new file mode 100644 index 0000000000..2efc3a6b4a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent.html @@ -0,0 +1,18 @@ +<!doctype html> +<meta charset=utf-8> +<title>Set location from a parent</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id=log></div> +<iframe></iframe> +<script> + setup({ single_test: true }); + onload = function() { + var fr = document.querySelector("iframe") + fr.contentWindow.location = "support/dummy.html" + fr.onload = function() { + assert_equals(fr.contentDocument.referrer, document.URL) + done() + } + } +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-src.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-src.html new file mode 100644 index 0000000000..e21260cd35 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-src.html @@ -0,0 +1,18 @@ +<!doctype html> +<meta charset=utf-8> +<title>Set src from a function called from a parent</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id=log></div> +<iframe src="support/set-parent-src.html"></iframe> +<script> +async_test(function() { + onload = this.step_func(function() { + var fr = document.querySelector("iframe") + fr.contentWindow.go() + fr.onload = this.step_func_done(function() { + assert_equals(fr.contentDocument.referrer, document.URL) + }) + }) +}) +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function.html new file mode 100644 index 0000000000..c6fa765b89 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function.html @@ -0,0 +1,19 @@ +<!doctype html> +<meta charset=utf-8> +<title>Set location from a function called from a parent</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id=log></div> +<iframe src="support/location-set.html"></iframe> +<script> +async_test(function() { + onload = this.step_func(function() { + var fr = document.querySelector("iframe") + var url = fr.contentDocument.URL + fr.contentWindow.go() + fr.onload = this.step_func_done(function() { + assert_equals(fr.contentDocument.referrer, url) + }) + }) +}) +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-src-about-blank.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-src-about-blank.html new file mode 100644 index 0000000000..479019d847 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-src-about-blank.html @@ -0,0 +1,20 @@ +<!doctype html> +<meta charset=utf-8> +<title>Set the src attribute to about:blank and check referrer</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id=log></div> +<iframe></iframe> +<script> + setup({ + single_test: true + }); + onload = function() { + var fr = document.querySelector("iframe") + fr.src = "about:blank" + fr.onload = function() { + assert_equals(fr.contentDocument.referrer, document.location.origin + '/') + done() + } + } +</script> diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/dummy.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/dummy.html new file mode 100644 index 0000000000..0638657093 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/dummy.html @@ -0,0 +1,3 @@ +<!doctype html> +<meta charset=utf-8> +<p>Hello.
\ No newline at end of file diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/location-set.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/location-set.html new file mode 100644 index 0000000000..ad733afac3 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/location-set.html @@ -0,0 +1,8 @@ +<!doctype html> +<meta charset=utf-8> +<script> + function go() { + location.href = "support/dummy.html" + } +</script> +<p>Hello. Go.
\ No newline at end of file diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/set-parent-src.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/set-parent-src.html new file mode 100644 index 0000000000..9d45be8c8d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/set-parent-src.html @@ -0,0 +1,8 @@ +<!doctype html> +<meta charset=utf-8> +<script> + function go() { + frameElement.src = "support/dummy.html" + } +</script> +<p>Hello. Go.
\ No newline at end of file diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/top-level-data-url.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/top-level-data-url.window.js new file mode 100644 index 0000000000..ca321f106a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/top-level-data-url.window.js @@ -0,0 +1,20 @@ +// META: timeout=long + +const dataURL = `data:text/html,...`; +const encodedDataURL = encodeURIComponent(dataURL); + +[dataURL, `resources/redirect.py?location=${encodedDataURL}`].forEach(url => { + [undefined, "opener", "noopener", "noreferrer"].forEach(opener => { + async_test(t => { + const popup = window.open(url, "", opener); + t.step_timeout(() => { + if (opener === "noopener" || opener == "noreferrer") { + assert_equals(popup, null); + } else { + assert_true(popup.closed); + } + t.done(); + }, 1500); + }, `Navigating a popup using window.open("${url}", "", "${opener}")`); + }); +}); |