diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /testing/web-platform/tests/navigation-api/navigation-methods | |
parent | Initial commit. (diff) | |
download | firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/navigation-api/navigation-methods')
123 files changed, 3124 insertions, 0 deletions
diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/back-forward-multiple-frames.html b/testing/web-platform/tests/navigation-api/navigation-methods/back-forward-multiple-frames.html new file mode 100644 index 0000000000..738bfd37dc --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/back-forward-multiple-frames.html @@ -0,0 +1,74 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + // Step 1 + assert_equals(navigation.entries().length, 1, "step 1 outer entries() length"); + assert_equals(i.contentWindow.navigation.entries().length, 1, "step 1 iframe entries() length"); + await navigation.navigate("#top").committed; + // Step 2: iframe at initial entry, top on second entry + assert_equals(navigation.entries().length, 2, "step 2 outer entries() length"); + assert_equals(i.contentWindow.navigation.entries().length, 1, "step 2 iframe entries() length"); + await i.contentWindow.navigation.navigate("#iframe").committed; + + // Step 3: Both windows on second entry. + assert_equals(navigation.entries().length, 2, "step 3 outer entries() length"); + assert_equals(i.contentWindow.navigation.entries().length, 2, "step 3 iframe entries() length"); + assert_equals(navigation.currentEntry.index, 1, "step 3 outer index"); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1, "step 1 iframe index"); + + // NOTE: the order of navigation in the two windows is not guaranteed; we need to wait for both. + + // Going back in the iframe should go 3->2 (navigating iframe only) + await Promise.all([ + i.contentWindow.navigation.back().committed, + new Promise(resolve => i.contentWindow.onpopstate = resolve) + ]); + assert_equals(navigation.currentEntry.index, 1, "after iframe back() outer index"); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0, "after iframe back() iframe index"); + + // Going forward in iframe should go 2->3 + await Promise.all([ + i.contentWindow.navigation.forward().commited, + new Promise(resolve => i.contentWindow.onpopstate = resolve) + ]); + assert_equals(navigation.currentEntry.index, 1, "after iframe forward() outer index"); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1, "after iframe forward() iframe index"); + + // Going back in top should go 3->1 (navigating both windows). + await Promise.all([ + navigation.back().commited, + new Promise(resolve => i.contentWindow.onpopstate = resolve) + ]); + assert_equals(navigation.currentEntry.index, 0, "after outer back() outer index"); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0, "after outer back() iframe index"); + + // Next two should not navigate the iframe + i.contentWindow.onpopstate = t.unreached_func("popstate must not be called"); + + // Going forward in top should go 1->2 (navigating top only) + await navigation.forward().committed; + await new Promise(resolve => t.step_timeout(resolve, 0)); + assert_equals(navigation.currentEntry.index, 1, "after outer forward() outer index"); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0, "after outer forward() iframe index"); + + // Going back in top should go 2->1 + await navigation.back().committed; + await new Promise(resolve => t.step_timeout(resolve, 0)); + assert_equals(navigation.currentEntry.index, 0, "after outer second back() outer index"); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0, "after outer second back() iframe index"); + + // Going forward in iframe should go 1->3 (navigating both windows) + await Promise.all([ + i.contentWindow.navigation.forward().commited, + new Promise(resolve => i.contentWindow.onpopstate = resolve) + ]); + assert_equals(navigation.currentEntry.index, 1, "after iframe second forward() outer index"); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1, "after iframe second forward() iframe index"); +}, "navigation.back() and navigation.forward() can navigate multiple frames"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/disambigaute-back.html b/testing/web-platform/tests/navigation-api/navigation-methods/disambigaute-back.html new file mode 100644 index 0000000000..d44d435896 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/disambigaute-back.html @@ -0,0 +1,32 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +async_test(t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + window.onload = () => t.step_timeout(() => { + assert_equals(navigation.entries().length, 1); + assert_equals(i.contentWindow.navigation.entries().length, 1); + navigation.navigate("#top"); + assert_equals(navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.entries().length, 1); + i.contentWindow.navigation.navigate("#1"); + assert_equals(navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(navigation.currentEntry.index, 1); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1); + assert_true(navigation.canGoBack); + assert_true(i.contentWindow.navigation.canGoBack); + + // There are 2 joint session history entries containing the iframe's + // previous key. Navigate to the nearest one (which navigates the iframe + // but not the top window). + i.contentWindow.navigation.back().committed.then(t.step_func_done(() => { + assert_equals(navigation.currentEntry.index, 1); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0); + })); + }, 0); +}, "navigation.back() goes to the nearest back entry"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/disambigaute-forward.html b/testing/web-platform/tests/navigation-api/navigation-methods/disambigaute-forward.html new file mode 100644 index 0000000000..e252e30994 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/disambigaute-forward.html @@ -0,0 +1,40 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +async_test(t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + window.onload = () => t.step_timeout(() => { + assert_equals(navigation.entries().length, 1); + assert_equals(i.contentWindow.navigation.entries().length, 1); + navigation.navigate("#top"); + assert_equals(navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.entries().length, 1); + i.contentWindow.navigation.navigate("#1"); + assert_equals(navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(navigation.currentEntry.index, 1); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1); + assert_true(navigation.canGoBack); + assert_true(i.contentWindow.navigation.canGoBack); + + i.contentWindow.navigation.back().committed.then(t.step_func(() => { + assert_equals(navigation.currentEntry.index, 1); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0); + navigation.back().committed.then(t.step_func(() => { + assert_equals(navigation.currentEntry.index, 0); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0); + // There are 2 joint session history entries containing the top window's + // final key. Navigate to the nearest one (which navigates only the + // top window). + navigation.forward().committed.then(t.step_func_done(() => { + assert_equals(navigation.currentEntry.index, 1); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0); + })); + })); + })); + }, 0); +}, "navigation.forward() goes to the nearest forward entry"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/disambigaute-traverseTo-back-multiple.html b/testing/web-platform/tests/navigation-api/navigation-methods/disambigaute-traverseTo-back-multiple.html new file mode 100644 index 0000000000..03a8eb10ed --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/disambigaute-traverseTo-back-multiple.html @@ -0,0 +1,35 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +async_test(t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + window.onload = () => t.step_timeout(() => { + assert_equals(navigation.entries().length, 1); + assert_equals(i.contentWindow.navigation.entries().length, 1); + navigation.navigate("#top"); + assert_equals(navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.entries().length, 1); + + let iframe_initial_key = i.contentWindow.navigation.currentEntry.key; + i.contentWindow.navigation.navigate("#1"); + i.contentWindow.navigation.navigate("#2"); + assert_equals(navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.entries().length, 3); + assert_equals(navigation.currentEntry.index, 1); + assert_equals(i.contentWindow.navigation.currentEntry.index, 2); + assert_true(navigation.canGoBack); + assert_true(i.contentWindow.navigation.canGoBack); + + // There are 2 joint session history entries containing the iframe's + // initial key. Navigate to the nearest one (which navigates the iframe + // but not the top window). + i.contentWindow.navigation.traverseTo(iframe_initial_key).committed.then(t.step_func_done(() => { + assert_equals(navigation.currentEntry.index, 1); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0); + })); + }, 0); +}, "navigation.traverseTo() goes to the nearest entry when going back"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/disambigaute-traverseTo-forward-multiple.html b/testing/web-platform/tests/navigation-api/navigation-methods/disambigaute-traverseTo-forward-multiple.html new file mode 100644 index 0000000000..f8e78c4b1d --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/disambigaute-traverseTo-forward-multiple.html @@ -0,0 +1,39 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + assert_equals(navigation.entries().length, 1); + assert_equals(i.contentWindow.navigation.entries().length, 1); + let initial_key = navigation.currentEntry.key; + await navigation.navigate("#top1").committed; + await navigation.navigate("#top2").committed; + assert_equals(navigation.entries().length, 3); + assert_equals(i.contentWindow.navigation.entries().length, 1); + await i.contentWindow.navigation.navigate("#1").committed; + assert_equals(navigation.entries().length, 3); + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(navigation.currentEntry.index, 2); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1); + assert_true(navigation.canGoBack); + assert_true(i.contentWindow.navigation.canGoBack); + let final_key = navigation.currentEntry.key; + + await i.contentWindow.navigation.back().committed; + assert_equals(navigation.currentEntry.index, 2); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0); + await navigation.traverseTo(initial_key).committed; + assert_equals(navigation.currentEntry.index, 0); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0); + // There are 2 joint session history entries containing the top window's + // final key. Navigate to the nearest one (which navigates only the + // top window). + await navigation.traverseTo(final_key).committed; + assert_equals(navigation.currentEntry.index, 2); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0); +}, "navigation.traverseTo() goes to the nearest entry when going forward"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/forward-to-pruned-entry.html b/testing/web-platform/tests/navigation-api/navigation-methods/forward-to-pruned-entry.html new file mode 100644 index 0000000000..05f70c5c2d --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/forward-to-pruned-entry.html @@ -0,0 +1,33 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + await navigation.navigate("#foo").finished; + assert_equals(navigation.entries().length, 2); + await navigation.back().finished; + assert_equals(navigation.currentEntry.index, 0); + + // Traverse forward then immediately do a same-document push. This will + // truncate the back forward list, and by the time the traverse commits, the + // destination key will no longer be present in navigation.entries(). The + // traverse should abort. Because the traverse aborts before the navigate + // event fires, the navigateerror event should not fire. + navigation.onnavigateerror = t.unreached_func("navigateerror should not fire"); + let forward_value = navigation.forward(); + await navigation.navigate("#clobber").finished; + assert_equals(navigation.currentEntry.index, 1); + await promise_rejects_dom(t, "AbortError", forward_value.committed); + await promise_rejects_dom(t, "AbortError", forward_value.finished); + + // This leaves navigation.entries() in a consistent state where traversing + // back and forward still works. + await navigation.back().finished; + assert_equals(navigation.currentEntry.index, 0); + await navigation.forward().finished; + assert_equals(navigation.currentEntry.index, 1); +}, "If forward pruning clobbers the target of a traverse, abort"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-base-url.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-base-url.html new file mode 100644 index 0000000000..00fefa6608 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-base-url.html @@ -0,0 +1,15 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="resources/page-with-base-url-common.html"></iframe> +<script> +async_test(t => { + window.onload = t.step_func(() => { + i.contentWindow.navigation.navigate("blank.html"); + i.onload = t.step_func_done(() => { + const iframeURL = new URL(i.contentWindow.navigation.currentEntry.url); + assert_equals(iframeURL.pathname, "/common/blank.html"); + }); + }); +}, "navigate() must resolve URLs relative to navigation object's base URL"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-from-initial-about-blank-gc.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-from-initial-about-blank-gc.html new file mode 100644 index 0000000000..d35121ec81 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-from-initial-about-blank-gc.html @@ -0,0 +1,18 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/gc.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<!-- + Regression test for https://bugs.chromium.org/p/chromium/issues/detail?id=1289864. +--> + +<script> +promise_test(t => { + i.contentWindow.navigation.navigate("/common/blank.html?1"); + + return garbageCollect(); +}, `navigate() from <iframe> with src="" but still on initial about:blank doesn't cause a crash on GC`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-from-initial-about-blank-src.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-from-initial-about-blank-src.html new file mode 100644 index 0000000000..8e54943669 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-from-initial-about-blank-src.html @@ -0,0 +1,21 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> + +<!-- + Sort of a regression test for https://bugs.chromium.org/p/chromium/issues/detail?id=1289864, + but since that is GC-dependent this will probably not fail in codebases that exhibit that bug. + So it's really just adding some extra general coverage for navigation.navigate(). +--> + +<script> +async_test(t => { + i.contentWindow.navigation.navigate("/common/blank.html?1"); + i.onload = t.step_func_done(() => { + const iframeURL = new URL(i.contentWindow.navigation.currentEntry.url); + assert_equals(iframeURL.pathname, "/common/blank.html"); + assert_equals(iframeURL.search, "?1"); + }); +}, `navigate() from <iframe> with src="" but still on initial about:blank works`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-from-initial-about-blank.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-from-initial-about-blank.html new file mode 100644 index 0000000000..00b2216166 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-from-initial-about-blank.html @@ -0,0 +1,22 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i"></iframe> + +<!-- + Sort of a regression test for https://bugs.chromium.org/p/chromium/issues/detail?id=1289864, + but since that is GC-dependent this will probably not fail in codebases that exhibit that bug. + So it's really just adding some extra general coverage for navigation.navigate(). +--> + + +<script> +async_test(t => { + i.contentWindow.navigation.navigate("/common/blank.html?1"); + i.onload = t.step_func_done(() => { + const iframeURL = new URL(i.contentWindow.navigation.currentEntry.url); + assert_equals(iframeURL.pathname, "/common/blank.html"); + assert_equals(iframeURL.search, "?1"); + }); +}, `navigate() from <iframe> still on initial about:blank works`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-push-not-loaded.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-push-not-loaded.html new file mode 100644 index 0000000000..c1ecdaeeaf --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-push-not-loaded.html @@ -0,0 +1,20 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Purposefully do not wait until after the load event (unlike some sibling tests). + assert_equals(navigation.entries().length, 1); + assert_equals(document.readyState, "loading", "Document must not have loaded yet"); + + let navigateEventType; + navigation.onnavigate = e => navigateEventType = e.navigationType; + + await navigation.navigate("#1", { history: "push" }).finished; + assert_equals(navigateEventType, "push"); + assert_equals(navigation.entries().length, 2); + assert_equals(navigation.currentEntry.index, 1); +}, "navigate() with history: 'push' in a document that has not yet had its load event"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-push-same-url-cross-document.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-push-same-url-cross-document.html new file mode 100644 index 0000000000..2d07bdcede --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-push-same-url-cross-document.html @@ -0,0 +1,25 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation due to onload not having completed. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + assert_equals(i.contentWindow.navigation.entries().length, 1); + + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + let navigateEventType; + i.contentWindow.navigation.onnavigate = e => navigateEventType = e.navigationType; + + i.contentWindow.navigation.navigate(i.contentWindow.location, { history: "push" }); + await new Promise(resolve => i.onload = resolve); + assert_equals(navigateEventType, "push"); + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1); +}, "navigate() to the current URL with history: 'push' and allow it to go cross document"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-push-same-url.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-push-same-url.html new file mode 100644 index 0000000000..339e8c3056 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-push-same-url.html @@ -0,0 +1,26 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation due to onload not having completed. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + assert_equals(navigation.entries().length, 1); + + + let navigateEventType; + navigation.onnavigate = e => { + navigateEventType = e.navigationType; + e.intercept(); + } + navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + await navigation.navigate(location.href, { history: "push" }).finished; + assert_equals(navigateEventType, "push"); + assert_equals(navigation.entries().length, 2); + assert_equals(navigation.currentEntry.index, 1); +}, "navigate() to the current URL with history: 'push' and intercept so it remains same-document"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-state-replace.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-state-replace.html new file mode 100644 index 0000000000..bf0af521da --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-state-replace.html @@ -0,0 +1,16 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +promise_test(async () => { + history.replaceState("state1", "", "#1"); + assert_equals(history.state, "state1"); + + let onnavigate_called = false; + navigation.onnavigate = () => onnavigate_called = true; + await navigation.navigate("#2", { history: "replace" }).committed; + assert_equals(location.hash, "#2"); + assert_true(onnavigate_called); + assert_equals(history.state, null); +}, "history.state should be nulled by navigate()"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-state.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-state.html new file mode 100644 index 0000000000..0541636de5 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-history-state.html @@ -0,0 +1,16 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +promise_test(async () => { + history.replaceState("state1", "", "#1"); + assert_equals(history.state, "state1"); + + let onnavigate_called = false; + navigation.onnavigate = () => onnavigate_called = true; + await navigation.navigate("#2").committed; + assert_equals(location.hash, "#2"); + assert_true(onnavigate_called); + assert_equals(history.state, null); +}, "history.state should be nulled by navigate()"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-info-and-state.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-info-and-state.html new file mode 100644 index 0000000000..828d7daea5 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-info-and-state.html @@ -0,0 +1,18 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +promise_test(async t => { + let navInfo = { nav : "info" }; + let navState = { statevar: "state" }; + let onnavigated_called = false; + navigation.onnavigate = t.step_func(e => { + onnavigated_called = true; + assert_equals(e.info, navInfo) + }); + await navigation.navigate("#1", { info: navInfo, state: navState }).committed; + assert_true(onnavigated_called); + assert_not_equals(navigation.currentEntry.getState(), navState); + assert_equals(navigation.currentEntry.getState().statevar, navState.statevar); +}, "navigate() with info and state"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-intercept-history-state.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-intercept-history-state.html new file mode 100644 index 0000000000..7faf6ffb5a --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-intercept-history-state.html @@ -0,0 +1,14 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +promise_test(async () => { + history.replaceState("state1", "", "#1"); + assert_equals(history.state, "state1"); + + navigation.onnavigate = e => e.intercept({ handler: () => Promise.resolve("r") }); + await navigation.navigate("#2").committed; + assert_equals(location.hash, "#2"); + assert_equals(history.state, null); +}, "history.story should be nulled by navigate() handled by intercept()"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-relative-url.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-relative-url.html new file mode 100644 index 0000000000..cc95d5e003 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-relative-url.html @@ -0,0 +1,16 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +async_test(t => { + window.onload = t.step_func(() => { + i.contentWindow.navigation.navigate("?1"); + i.onload = t.step_func_done(() => { + let iframe_url = new URL(i.contentWindow.navigation.currentEntry.url); + assert_equals(iframe_url.search, "?1"); + assert_equals(iframe_url.pathname, "/common/blank.html"); + }); + }); +}, "navigate() should resolve urls relative to navigation object, not the caller"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-replace-cross-document.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-replace-cross-document.html new file mode 100644 index 0000000000..7f016babe6 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-replace-cross-document.html @@ -0,0 +1,29 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +async_test(t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation (which would defeat the point of the test). + window.onload = () => t.step_timeout(() => { + let start_history_length = history.length; + let start_entry_top = navigation.currentEntry; + let start_entry_iframe_id = i.contentWindow.navigation.currentEntry.id; + let start_entry_iframe_key = i.contentWindow.navigation.currentEntry.key; + + i.contentWindow.navigation.navigate("?1", { history: "replace" }); + i.onload = t.step_func_done(() => { + assert_equals(history.length, start_history_length); + + assert_equals(navigation.entries().length, 1); + assert_equals(navigation.currentEntry, start_entry_top); + + assert_equals(i.contentWindow.navigation.entries().length, 1); + let iframe_entry = i.contentWindow.navigation.currentEntry; + assert_not_equals(start_entry_iframe_id, iframe_entry.id); + assert_equals(start_entry_iframe_key, iframe_entry.key); + }); + }, 0); +}, "navigate() with history: 'replace' option"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-replace-same-document.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-replace-same-document.html new file mode 100644 index 0000000000..0d8493b788 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-replace-same-document.html @@ -0,0 +1,21 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +async_test(t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + window.onload = () => t.step_timeout(t.step_func_done(async () => { + let start_history_length = history.length; + let key1 = navigation.currentEntry.key; + await navigation.navigate("#1").committed; + let key2 = navigation.currentEntry.key; + assert_not_equals(key1, key2); + await navigation.navigate("#2", { history: "replace" }).committed; + let key3 = navigation.currentEntry.key; + assert_equals(key2, key3); + assert_equals(navigation.entries().length, 2); + assert_equals(history.length, start_history_length + 1); + }), 0); +}, "navigate() with history: 'replace' option"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-same-document.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-same-document.html new file mode 100644 index 0000000000..9ffd8248f8 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-same-document.html @@ -0,0 +1,23 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="d"></div> +<script> +promise_test(async () => { + let onnavigate_called = false; + navigation.onnavigate = () => onnavigate_called = true; + await navigation.navigate("#d").committed; + assert_equals(location.hash, "#d"); + assert_true(onnavigate_called); + assert_equals(document.querySelector(":target"), d); +}, "navigate() navigates same-document and fires onnavigate (async)"); + +test(() => { + let onnavigate_called = false; + navigation.onnavigate = () => onnavigate_called = true; + navigation.navigate("#d"); + assert_equals(location.hash, "#d"); + assert_true(onnavigate_called); + assert_equals(document.querySelector(":target"), d); +}, "navigate() navigates same-document and fires onnavigate (sync)"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-state-repeated-await.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-state-repeated-await.html new file mode 100644 index 0000000000..539b3dc1da --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-state-repeated-await.html @@ -0,0 +1,13 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +promise_test(async t => { + navigation.onnavigate = e => e.intercept(); + + await navigation.navigate('/foo', {state: {foo: 1}}).committed; + assert_equals(navigation.currentEntry.getState().foo, 1); + await navigation.navigate('/foo', {state: {foo: 2}}).committed; + assert_equals(navigation.currentEntry.getState().foo, 2); +}, "navigate() with state should work correctly when called repeatedly - with awaits"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/navigate-state-repeated.html b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-state-repeated.html new file mode 100644 index 0000000000..227bbb0cfc --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/navigate-state-repeated.html @@ -0,0 +1,20 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +promise_test(async t => { + navigation.onnavigate = e => e.intercept(); + + let result1 = navigation.navigate('/foo', {state: {foo: 1}}); + assert_equals(navigation.currentEntry.getState().foo, 1); + + // Don't let the harness fail because of the "AbortError" DOMException that is caused by interrupting the navigation. + result1.finished.catch(() => {}); + + let result2 = navigation.navigate('/foo', {state: {foo: 2}}); + assert_equals(navigation.currentEntry.getState().foo, 2); + + await result1.committed; + await result2.committed; +}, "navigate() with state should work correctly when called repeatedly"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/reload-base-url.html b/testing/web-platform/tests/navigation-api/navigation-methods/reload-base-url.html new file mode 100644 index 0000000000..1e8d3b90c3 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/reload-base-url.html @@ -0,0 +1,16 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="resources/page-with-base-url-common.html"></iframe> +<script> +async_test(t => { + window.onload = t.step_func(() => { + const startingURL = new URL(i.src); + i.contentWindow.navigation.reload(); + i.onload = t.step_func_done(() => { + const iframeURL = new URL(i.contentWindow.navigation.currentEntry.url); + assert_equals(iframeURL.pathname, startingURL.pathname); + }); + }); +}, "reload() must ignore the Navigation object's base URL"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/reload-info.html b/testing/web-platform/tests/navigation-api/navigation-methods/reload-info.html new file mode 100644 index 0000000000..637f6976ad --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/reload-info.html @@ -0,0 +1,47 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +async_test(t => { + window.onload = t.step_func(() => { + const navState = { key: "value" }; + const navInfo = { infoKey: "infoValue" }; + i.contentWindow.navigation.navigate("#1", { state: navState }).committed.then(t.step_func(() => { + // Make sure that state setting worked + assert_equals(i.contentWindow.navigation.currentEntry.getState().key, "value"); + assert_not_equals(i.contentWindow.navigation.currentEntry.getState(), navState); + + let start_url = i.contentWindow.location.href; + let start_key = i.contentWindow.navigation.currentEntry.key; + let start_id = i.contentWindow.navigation.currentEntry.id; + let start_state = i.contentWindow.navigation.currentEntry.getState(); + let onnavigate_called = false; + let promise_settled = false; + i.contentWindow.navigation.onnavigate = t.step_func(e => { + onnavigate_called = true; + assert_equals(e.info, navInfo); + assert_equals(e.navigationType, "reload"); + assert_equals(e.destination.getState().key, "value"); + assert_not_equals(e.destination.getState(), start_state); + }); + i.contentWindow.navigation.reload({ info: navInfo }).committed.finally(() => { + promise_settled = true; + }); + i.onload = t.step_func(() => { + assert_true(onnavigate_called); + assert_equals(i.contentWindow.location.href, start_url); + assert_equals(i.contentWindow.navigation.currentEntry.key, start_key); + assert_equals(i.contentWindow.navigation.currentEntry.id, start_id); + assert_equals(i.contentWindow.navigation.currentEntry.getState().key, "value"); + assert_not_equals(i.contentWindow.navigation.currentEntry.getState(), start_state); + assert_equals(i.contentWindow.navigation.entries().length, 2); + + t.step_timeout(t.step_func_done(() => { + assert_equals(promise_settled, false); + }), 0); + }); + })); + }); +}, "reload() variant with only info"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/reload-navigation-timing.html b/testing/web-platform/tests/navigation-api/navigation-methods/reload-navigation-timing.html new file mode 100644 index 0000000000..ce03e7a7ca --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/reload-navigation-timing.html @@ -0,0 +1,15 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + i.contentWindow.navigation.reload(); + await new Promise(resolve => i.onload = resolve); + + const entries = i.contentWindow.performance.getEntriesByType("navigation"); + assert_equals(entries.length, 1); + assert_equals(entries[0].type, "reload"); +}, `reload() appears as a reload to navigation timing APIs`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/reload-no-args.html b/testing/web-platform/tests/navigation-api/navigation-methods/reload-no-args.html new file mode 100644 index 0000000000..c94eae0b93 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/reload-no-args.html @@ -0,0 +1,46 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +async_test(t => { + window.onload = t.step_func(() => { + const navState = { key: "value" }; + i.contentWindow.navigation.navigate("#1", { state: navState }).committed.then(t.step_func(() => { + // Make sure that state setting worked + assert_equals(i.contentWindow.navigation.currentEntry.getState().key, "value"); + assert_not_equals(i.contentWindow.navigation.currentEntry.getState(), navState); + + let start_url = i.contentWindow.location.href; + let start_key = i.contentWindow.navigation.currentEntry.key; + let start_id = i.contentWindow.navigation.currentEntry.id; + let start_state = i.contentWindow.navigation.currentEntry.getState(); + let onnavigate_called = false; + let promise_settled = false; + i.contentWindow.navigation.onnavigate = t.step_func(e => { + onnavigate_called = true; + assert_equals(e.info, undefined); + assert_equals(e.navigationType, "reload"); + assert_equals(e.destination.getState().key, "value"); + assert_not_equals(e.destination.getState(), start_state); + }); + i.contentWindow.navigation.reload().committed.finally(() => { + promise_settled = true; + }); + i.onload = t.step_func(() => { + assert_true(onnavigate_called); + assert_equals(i.contentWindow.location.href, start_url); + assert_equals(i.contentWindow.navigation.currentEntry.key, start_key); + assert_equals(i.contentWindow.navigation.currentEntry.id, start_id); + assert_equals(i.contentWindow.navigation.currentEntry.getState().key, "value"); + assert_not_equals(i.contentWindow.navigation.currentEntry.getState(), start_state); + assert_equals(i.contentWindow.navigation.entries().length, 2); + + t.step_timeout(t.step_func_done(() => { + assert_equals(promise_settled, false); + }), 0); + }); + })); + }); +}, "reload() variant with no state or info"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/reload-service-worker-fetch-event.html b/testing/web-platform/tests/navigation-api/navigation-methods/reload-service-worker-fetch-event.html new file mode 100644 index 0000000000..67535c70bf --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/reload-service-worker-fetch-event.html @@ -0,0 +1,28 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script> + +<!-- Keep an eye on https://github.com/whatwg/fetch/issues/1280 --> + +<script> +const worker = "resources/fetch-event-test-worker.js"; + +promise_test(async t => { + const scope = "resources/service-worker-page.html?reload-service-worker-fetch-event"; + + const reg = await service_worker_unregister_and_register(t, worker, scope); + + await wait_for_state(t, reg.installing, "activated"); + const frame = await with_iframe(scope); + assert_equals(frame.contentDocument.body.textContent, "method = GET, isReloadNavigation = false"); + + frame.contentWindow.navigation.reload(); + await new Promise(resolve => frame.addEventListener("load", resolve, { once: true })); + assert_equals(frame.contentDocument.body.textContent, "method = GET, isReloadNavigation = true"); + + frame.remove(); + await reg.unregister(); +}, "reload() appears as a reload to service worker fetch event handlers"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/reload-state-and-info.html b/testing/web-platform/tests/navigation-api/navigation-methods/reload-state-and-info.html new file mode 100644 index 0000000000..1ba26ae3da --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/reload-state-and-info.html @@ -0,0 +1,40 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +async_test(t => { + window.onload = t.step_func(() => { + const navState1 = { key: "value" }; + const navState2 = { key2: "value2" }; + const navInfo = { infoKey: "infoValue" }; + i.contentWindow.navigation.navigate("#1", { state: navState1 }).committed.then(t.step_func(() => { + // Make sure that state setting worked + assert_equals(i.contentWindow.navigation.currentEntry.getState().key, "value", "initial state setup"); + assert_not_equals(i.contentWindow.navigation.currentEntry.getState(), navState1); + + let start_url = i.contentWindow.location.href; + let start_key = i.contentWindow.navigation.currentEntry.key; + let start_id = i.contentWindow.navigation.currentEntry.id; + let onnavigate_called = false; + let promise_settled = false; + i.contentWindow.navigation.onnavigate = t.step_func(e => { + e.intercept(); + onnavigate_called = true; + assert_equals(e.info, navInfo); + assert_equals(e.navigationType, "reload"); + assert_equals(e.destination.getState().key2, "value2", "navigate event for the reload()"); + assert_not_equals(e.destination.getState(), navState2); + }); + i.contentWindow.navigation.reload({ info: navInfo, state: navState2 }).committed.then(t.step_func_done(() => { + assert_true(onnavigate_called); + assert_equals(i.contentWindow.location.href, start_url); + assert_equals(i.contentWindow.navigation.currentEntry.key, start_key); + assert_equals(i.contentWindow.navigation.currentEntry.id, start_id); + assert_equals(i.contentWindow.navigation.currentEntry.getState().key2, "value2", "currentEntry.getState() after the reload"); + assert_not_equals(i.contentWindow.navigation.currentEntry.getState(), navState2); + })); + })); + }); +}, "reload() variant with info and new state"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/reload-state-undefined.html b/testing/web-platform/tests/navigation-api/navigation-methods/reload-state-undefined.html new file mode 100644 index 0000000000..b8b34650ab --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/reload-state-undefined.html @@ -0,0 +1,39 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +async_test(t => { + window.onload = t.step_func(() => { + const navState = { key: "value" }; + const navInfo = { infoKey: "infoValue" }; + i.contentWindow.navigation.navigate("#1", { state: navState }).committed.then(t.step_func(() => { + // Make sure that state setting worked + assert_equals(i.contentWindow.navigation.currentEntry.getState().key, "value"); + assert_not_equals(i.contentWindow.navigation.currentEntry.getState(), navState); + + let start_url = i.contentWindow.location.href; + let start_key = i.contentWindow.navigation.currentEntry.key; + let start_id = i.contentWindow.navigation.currentEntry.id; + let onnavigate_called = false; + let promise_settled = false; + i.contentWindow.navigation.onnavigate = t.step_func(e => { + e.intercept(); + onnavigate_called = true; + assert_equals(e.info, navInfo); + assert_equals(e.navigationType, "reload"); + assert_equals(e.destination.getState().key, "value", "destination.getState()"); + assert_not_equals(e.destination.getState(), navState); + }); + i.contentWindow.navigation.reload({ info: navInfo, state: undefined }).committed.then(t.step_func_done(() => { + assert_true(onnavigate_called); + assert_equals(i.contentWindow.location.href, start_url); + assert_equals(i.contentWindow.navigation.currentEntry.key, start_key); + assert_equals(i.contentWindow.navigation.currentEntry.id, start_id); + assert_equals(i.contentWindow.navigation.currentEntry.getState().key, "value", "destination.getState()"); + assert_not_equals(i.contentWindow.navigation.currentEntry.getState(), navState); + })); + })); + }); +}, "reload() variant with info and state: undefined counts the same as not present (because of Web IDL dictionary semantics), so preserves the state"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/resources/fetch-event-test-worker.js b/testing/web-platform/tests/navigation-api/navigation-methods/resources/fetch-event-test-worker.js new file mode 100644 index 0000000000..98b7dd0fb5 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/resources/fetch-event-test-worker.js @@ -0,0 +1,7 @@ +self.addEventListener('fetch', function(event) { + const request = event.request; + const body = + `method = ${request.method}, ` + + `isReloadNavigation = ${request.isReloadNavigation}`; + event.transitionWhile(new Response(body)); +}); diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/resources/navigate-parent.html b/testing/web-platform/tests/navigation-api/navigation-methods/resources/navigate-parent.html new file mode 100644 index 0000000000..8584bb5774 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/resources/navigate-parent.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<script> +window.navigateParent = () => { + return parent.navigation.navigate("#2"); +}; +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/resources/navigate-sibling.html b/testing/web-platform/tests/navigation-api/navigation-methods/resources/navigate-sibling.html new file mode 100644 index 0000000000..29a5ee7390 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/resources/navigate-sibling.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<script> +window.navigateSibling = () => { + return parent.frames[0].navigation.navigate("/common/blank.html?2"); +}; +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/resources/navigation-back.html b/testing/web-platform/tests/navigation-api/navigation-methods/resources/navigation-back.html new file mode 100644 index 0000000000..a1441801ac --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/resources/navigation-back.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<script> +window.doNavigationBack = () => { + return navigation.back(); +}; +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/resources/page-with-base-url-common.html b/testing/web-platform/tests/navigation-api/navigation-methods/resources/page-with-base-url-common.html new file mode 100644 index 0000000000..8d9fedcc2b --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/resources/page-with-base-url-common.html @@ -0,0 +1,2 @@ +<!DOCTYPE html> +<base href="/common/"> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/resources/service-worker-page.html b/testing/web-platform/tests/navigation-api/navigation-methods/resources/service-worker-page.html new file mode 100644 index 0000000000..a10ff35dce --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/resources/service-worker-page.html @@ -0,0 +1,2 @@ +<!DOCTYPE html> +<p>We can't use /common/blank.html because of scope restrictions. diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/resources/slow-no-store.py b/testing/web-platform/tests/navigation-api/navigation-methods/resources/slow-no-store.py new file mode 100644 index 0000000000..48e5fc0266 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/resources/slow-no-store.py @@ -0,0 +1,6 @@ +import time + +def main(request, response): + # Sleep for 1sec + time.sleep(1) + response.headers.set(b"Cache-Control", b"no-cache, no-store, must-revalidate"); diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-204-205-download.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-204-205-download.html new file mode 100644 index 0000000000..5bedbf21e8 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-204-205-download.html @@ -0,0 +1,52 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> +<script src="/common/utils.js"></script> + +<body> +<script> +const tests = [ + ["204s", "204"], + ["205s", "205"], + ["Content-Disposition: attachment responses", "download"] +]; + +for (const [description, action] of tests) { + promise_test(async t => { + const id = token(); + + const i = document.createElement("iframe"); + i.src = `resources/204-205-download-on-second-visit.py?id=${id}`; + document.body.append(i); + await new Promise(r => i.onload = r); + + // Configure it to return a 204 on the next visit + await fetch(i.src + `&action=${action}`, { method: "POST" }); + + // Now navigate elsewhere + i.contentWindow.location.href = "/common/blank.html"; + await new Promise(r => i.onload = r); + + // Now try going back. It should do nothing (and not tell us about the result). + + const indexBefore = i.contentWindow.navigation.currentEntry.index; + + // One might be surprised that navigate does not fire. (It does fire for the + // corresponding tests of navigation.navigate(), i.e., this is + // traversal-specific behavior.) See https://github.com/WICG/navigation-api/issues/207 + // for some discussion. + i.contentWindow.navigation.onnavigate = t.unreached_func("onnavigate should not be called"); + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + const result = i.contentWindow.navigation.back(); + + assertNeverSettles(t, result, i.contentWindow); + + await new Promise(resolve => t.step_timeout(resolve, 50)); + assert_equals(i.contentWindow.navigation.currentEntry.index, indexBefore); + assert_equals(i.contentWindow.navigation.transition, null); + }, `back() promises to ${description} never settle`); +} +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-already-detached.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-already-detached.html new file mode 100644 index 0000000000..f9ff04f923 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-already-detached.html @@ -0,0 +1,30 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + assert_equals(i.contentWindow.navigation.entries().length, 1); + let key = i.contentWindow.navigation.currentEntry.key; + + i.contentWindow.navigation.navigate("?1"); + await new Promise(resolve => i.onload = resolve); + + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry, i.contentWindow.navigation.entries()[1]); + + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + + i.remove(); + + await assertBothRejectDOM(t, iWindow.navigation.back(), "InvalidStateError", iWindow, iDOMException); +}, "back() in a detached window"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-beforeunload.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-beforeunload.html new file mode 100644 index 0000000000..82c1f589cc --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-beforeunload.html @@ -0,0 +1,42 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + assert_equals(i.contentWindow.navigation.entries().length, 1); + let key = i.contentWindow.navigation.currentEntry.key; + + i.contentWindow.navigation.navigate("?1"); + await new Promise(resolve => i.onload = resolve); + + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry, i.contentWindow.navigation.entries()[1]); + + let navigateEventCount = 0; + i.contentWindow.navigation.onnavigate = () => navigateEventCount++; + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + let assertionPromise; + // The iframe does not have sticky activation, so per + // https://html.spec.whatwg.org/#prompt-to-unload-a-document, no prompt is + // shown and the navigation will proceed. + i.contentWindow.onbeforeunload = t.step_func(() => { + assertionPromise = assertBothRejectDOM(t, i.contentWindow.navigation.back(), "InvalidStateError", i.contentWindow); + }); + i.contentWindow.navigation.navigate("?1"); + + assert_not_equals(assertionPromise, undefined); + await assertionPromise; + + assert_equals(navigateEventCount, 1); +}, "back() inside onbeforeunload"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-forward-initial-about-blank.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-forward-initial-about-blank.html new file mode 100644 index 0000000000..dfdb6611ab --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-forward-initial-about-blank.html @@ -0,0 +1,25 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +promise_test(async t => { + let i = document.createElement("iframe"); + document.body.append(i); + + i.contentWindow.navigation.onnavigate = t.unreached_func("onnavigate should not be called"); + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + // Since there's no way to do a non-replacement navigation on the initial + // about:blank, there's no way to actually get in a situation where we're on + // about:blank but there's something else backward/forward in the history + // list. So this test will almost certainly pass just because there's nothing + // to go back/forward to. Oh well; it's still reasonable coverage. + + await assertBothRejectDOM(t, i.contentWindow.navigation.back(), "InvalidStateError", i.contentWindow); + await assertBothRejectDOM(t, i.contentWindow.navigation.forward(), "InvalidStateError", i.contentWindow); +}, "back() and forward() in initial about:blank document"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-forward-opaque-origin.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-forward-opaque-origin.html new file mode 100644 index 0000000000..59849e961d --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-forward-opaque-origin.html @@ -0,0 +1,9 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<iframe id="i" sandbox="allow-scripts" src="resources/back-forward-opaque-origin-page.html"></iframe> + +<script> +fetch_tests_from_window(i.contentWindow); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-forward-out-of-bounds.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-forward-out-of-bounds.html new file mode 100644 index 0000000000..015c090bf9 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-forward-out-of-bounds.html @@ -0,0 +1,16 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + assert_equals(navigation.entries().length, 1); + assert_equals(navigation.entries()[0], navigation.currentEntry); + assert_false(navigation.canGoBack); + assert_false(navigation.canGoForward); + + await assertBothRejectDOM(t, navigation.back(), "InvalidStateError"); + await assertBothRejectDOM(t, navigation.forward(), "InvalidStateError"); +}, "back() and forward() out of bounds"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-intercept-rejected.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-intercept-rejected.html new file mode 100644 index 0000000000..9d4238087f --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-intercept-rejected.html @@ -0,0 +1,28 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + location.href = "#1"; + + assert_equals(navigation.entries().length, 2); + const [entry0, entry1] = navigation.entries(); + assert_equals((new URL(entry0.url)).hash, ""); + assert_equals((new URL(entry1.url)).hash, "#1"); + + const err = new Error("boo!"); + const promise = Promise.reject(err); + promise.catch(() => {}); // prevent unhandled rejection testharness.js errors + navigation.onnavigate = e => e.intercept({ handler: () => promise }); + + const result = navigation.back(); + await assertCommittedFulfillsFinishedRejectsExactly(t, result, entry0, err); + assert_equals(navigation.currentEntry, entry0); +}, "back() promise rejection with rejected intercept()"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-intercept.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-intercept.html new file mode 100644 index 0000000000..2654045c2b --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back-intercept.html @@ -0,0 +1,25 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + location.href = "#1"; + + assert_equals(navigation.entries().length, 2); + const [entry0, entry1] = navigation.entries(); + assert_equals((new URL(entry0.url)).hash, ""); + assert_equals((new URL(entry1.url)).hash, "#1"); + + navigation.onnavigate = e => e.intercept({ handler: () => Promise.resolve({ abc: "def" }) }); + + const result = navigation.back(); + await assertBothFulfill(t, result, entry0); + assert_equals(navigation.currentEntry, entry0); +}, "back() and intercept() with a fulfilled promise"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back.html new file mode 100644 index 0000000000..a2b13db901 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/back.html @@ -0,0 +1,23 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + location.href = "#1"; + + assert_equals(navigation.entries().length, 2); + const [entry0, entry1] = navigation.entries(); + assert_equals((new URL(entry0.url)).hash, ""); + assert_equals((new URL(entry1.url)).hash, "#1"); + + const result = navigation.back(); + await assertBothFulfill(t, result, entry0); + assert_equals(navigation.currentEntry, entry0); +}, "back() promises"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward-already-detached.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward-already-detached.html new file mode 100644 index 0000000000..4dfa74d9f9 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward-already-detached.html @@ -0,0 +1,33 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + assert_equals(i.contentWindow.navigation.entries().length, 1); + let key = i.contentWindow.navigation.currentEntry.key; + + i.contentWindow.navigation.navigate("?1"); + await new Promise(resolve => i.onload = resolve); + + i.contentWindow.navigation.back(); + await new Promise(resolve => i.onload = resolve); + + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry, i.contentWindow.navigation.entries()[0]); + + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + + i.remove(); + + await assertBothRejectDOM(t, iWindow.navigation.forward(), "InvalidStateError", iWindow, iDOMException); +}, "forward() in a detached window"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward-beforeunload.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward-beforeunload.html new file mode 100644 index 0000000000..87fa4baa93 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward-beforeunload.html @@ -0,0 +1,45 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + assert_equals(i.contentWindow.navigation.entries().length, 1); + let key = i.contentWindow.navigation.currentEntry.key; + + i.contentWindow.navigation.navigate("?1"); + await new Promise(resolve => i.onload = resolve); + + i.contentWindow.navigation.back(); + await new Promise(resolve => i.onload = resolve); + + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry, i.contentWindow.navigation.entries()[0]); + + let navigateEventCount = 0; + i.contentWindow.navigation.onnavigate = () => navigateEventCount++; + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + let assertionPromise; + // The iframe does not have sticky activation, so per + // https://html.spec.whatwg.org/#prompt-to-unload-a-document, no prompt is + // shown and the navigation will proceed. + i.contentWindow.onbeforeunload = t.step_func(() => { + assertionPromise = assertBothRejectDOM(t, i.contentWindow.navigation.forward(), "InvalidStateError", i.contentWindow); + }); + i.contentWindow.navigation.navigate("?1"); + + assert_not_equals(assertionPromise, undefined); + await assertionPromise; + + assert_equals(navigateEventCount, 1); +}, "forward() inside onbeforeunload"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward-intercept-rejected.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward-intercept-rejected.html new file mode 100644 index 0000000000..9e86dcca19 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward-intercept-rejected.html @@ -0,0 +1,29 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + location.href = "#1"; + await navigation.back().committed; + + assert_equals(navigation.entries().length, 2); + const [entry0, entry1] = navigation.entries(); + assert_equals((new URL(entry0.url)).hash, ""); + assert_equals((new URL(entry1.url)).hash, "#1"); + + const err = new Error("boo!"); + const promise = Promise.reject(err); + promise.catch(() => {}); // prevent unhandled rejection testharness.js errors + navigation.onnavigate = e => e.intercept({ handler: () => promise }); + + const result = navigation.forward(); + await assertCommittedFulfillsFinishedRejectsExactly(t, result, entry1, err); + assert_equals(navigation.currentEntry, entry1); +}, "forward() promise rejection with rejected intercept()"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward-intercept.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward-intercept.html new file mode 100644 index 0000000000..ce4ab32170 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward-intercept.html @@ -0,0 +1,28 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + location.href = "#1"; + + assert_equals(navigation.entries().length, 2); + const [entry0, entry1] = navigation.entries(); + assert_equals((new URL(entry0.url)).hash, ""); + assert_equals((new URL(entry1.url)).hash, "#1"); + + await navigation.back().committed; + + const promise = Promise.resolve({ abc: "def" }); + navigation.onnavigate = e => e.intercept({ handler: () => promise }); + + const result = navigation.forward(); + await assertBothFulfill(t, result, entry1); + assert_equals(navigation.currentEntry, entry1); +}, "forward() and intercept() with a fulfilled promise"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward.html new file mode 100644 index 0000000000..36bdba8daa --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/forward.html @@ -0,0 +1,24 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + location.href = "#1"; + await navigation.back().committed; + + assert_equals(navigation.entries().length, 2); + const [entry0, entry1] = navigation.entries(); + assert_equals((new URL(entry0.url)).hash, ""); + assert_equals((new URL(entry1.url)).hash, "#1"); + + const result = navigation.forward(); + await assertBothFulfill(t, result, entry1); + assert_equals(navigation.currentEntry, entry1); +}, "forward() promises"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-204-205-download.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-204-205-download.html new file mode 100644 index 0000000000..f1e89b6940 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-204-205-download.html @@ -0,0 +1,42 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +const tests = [ + ["204s", "/common/blank.html?pipe=status(204)"], + ["205s", "/common/blank.html?pipe=status(205)"], + ["Content-Disposition: attachment responses", "/common/blank.html?pipe=header(Content-Disposition,attachment)"] +]; + +for (const [description, url] of tests) { + promise_test(async t => { + const i = document.createElement("iframe"); + i.src = "/common/blank.html"; + document.body.append(i); + await new Promise(resolve => i.onload = resolve); + + // This seems to be important? Without it the (outer) window load event + // doesn't fire, and the test harness hangs forever. This is probably a + // Chromium bug, but maybe a testharness bug, especially since explicit_done + // doesn't seem to help? + t.add_cleanup(() => i.remove()); + + let navigateCount = 0; + i.contentWindow.navigation.onnavigate = () => { ++navigateCount; }; + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + const result = i.contentWindow.navigation.navigate(url); + + assert_equals(navigateCount, 1); + assertNeverSettles(t, result, i.contentWindow); + + await new Promise(resolve => t.step_timeout(resolve, 50)); + assert_equals(i.contentWindow.location.href, i.src); + assert_equals(i.contentWindow.navigation.transition, null); + }, `navigate() promises to ${description} never settle`); +} +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-already-detached.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-already-detached.html new file mode 100644 index 0000000000..33cdd6922d --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-already-detached.html @@ -0,0 +1,19 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + + i.remove(); + + await assertBothRejectDOM(t, iWindow.navigation.navigate("?1"), "InvalidStateError", iWindow, iDOMException); +}, "navigate() in a detached window"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-beforeunload.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-beforeunload.html new file mode 100644 index 0000000000..f0a9069677 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-beforeunload.html @@ -0,0 +1,31 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + let navigateEventCount = 0; + i.contentWindow.navigation.onnavigate = () => navigateEventCount++; + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + let assertionPromise; + // The iframe does not have sticky activation, so per + // https://html.spec.whatwg.org/#prompt-to-unload-a-document, no prompt is + // shown and the navigation will proceed. + i.contentWindow.onbeforeunload = t.step_func(() => { + assertionPromise = assertBothRejectDOM(t, i.contentWindow.navigation.navigate("#"), "InvalidStateError", i.contentWindow); + }); + i.contentWindow.navigation.navigate("?1"); + + assert_not_equals(assertionPromise, undefined); + await assertionPromise; + + assert_equals(navigateEventCount, 1); +}, "navigate() inside onbeforeunload"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-cross-document.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-cross-document.html new file mode 100644 index 0000000000..b83b1e941f --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-cross-document.html @@ -0,0 +1,16 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + const result = i.contentWindow.navigation.navigate("?1"); + assertNeverSettles(t, result, i.contentWindow); + + await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0)); +}, "cross-document navigate() promises never settle"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-detach-in-onnavigate.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-detach-in-onnavigate.html new file mode 100644 index 0000000000..0e80e1b130 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-detach-in-onnavigate.html @@ -0,0 +1,18 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + i.contentWindow.navigation.onnavigate = () => i.remove(); + + await assertBothRejectDOM(t, i.contentWindow.navigation.navigate("#1"), "AbortError", iWindow, iDOMException); +}, "navigate() promise rejections when detaching an iframe inside onnavigate"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-detach-in-serialization.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-detach-in-serialization.html new file mode 100644 index 0000000000..14086c9f01 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-detach-in-serialization.html @@ -0,0 +1,24 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + + const trappedState = { + get prop() { + i.remove(); + return "whatever"; + } + }; + + await assertBothRejectDOM(t, i.contentWindow.navigation.navigate("#1", { state: trappedState }), "InvalidStateError", iWindow, iDOMException); +}, "navigate() promise rejections when detaching an iframe inside state serialization"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-file-url.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-file-url.html new file mode 100644 index 0000000000..138f6ffc12 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-file-url.html @@ -0,0 +1,10 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + await assertBothRejectDOM(t, navigation.navigate("file://"), "AbortError"); +}, "navigate() to a file: URL"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-initial-about-blank-cross-document.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-initial-about-blank-cross-document.html new file mode 100644 index 0000000000..8ec76b11dd --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-initial-about-blank-cross-document.html @@ -0,0 +1,21 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +promise_test(async t => { + let i = document.createElement("iframe"); + document.body.append(i); + + i.contentWindow.navigation.onnavigate = t.unreached_func("onnavigate should not be called"); + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + // Cannot just use "/common/blank.html?1" since it doesn't resolve relative to about:blank. + const result = i.contentWindow.navigation.navigate(new URL("/common/blank.html?1", location.href).href); + assertNeverSettles(t, result, i.contentWindow); + await new Promise(resolve => t.step_timeout(resolve, 10)); +}, "navigate() in initial about:blank document (cross-document)"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-initial-about-blank.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-initial-about-blank.html new file mode 100644 index 0000000000..9734a09d01 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-initial-about-blank.html @@ -0,0 +1,20 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +promise_test(async t => { + let i = document.createElement("iframe"); + document.body.append(i); + + i.contentWindow.navigation.onnavigate = t.unreached_func("onnavigate should not be called"); + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + const result = i.contentWindow.navigation.navigate("#1"); + assertNeverSettles(t, result, i.contentWindow); + await new Promise(resolve => t.step_timeout(resolve, 10)); +}, "navigate() in initial about:blank document"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-intercept-interrupted.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-intercept-interrupted.html new file mode 100644 index 0000000000..36403c892c --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-intercept-interrupted.html @@ -0,0 +1,23 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + navigation.addEventListener("navigate", e => e.intercept()); + + const result1 = navigation.navigate("#1"); + const result2 = navigation.navigate("#2"); + + assert_equals(navigation.entries().length, 3); + assert_array_equals(navigation.entries().map(e => (new URL(e.url)).hash), ["", "#1", "#2"]); + + await assertCommittedFulfillsFinishedRejectsDOM(t, result1, navigation.entries()[1], "AbortError"); + await assertBothFulfill(t, result2, navigation.currentEntry); +}, "interrupted navigate() promises with intercept()"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-intercept-rejected.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-intercept-rejected.html new file mode 100644 index 0000000000..79c8bd803e --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-intercept-rejected.html @@ -0,0 +1,17 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + const err = new Error("boo!"); + const promise = Promise.reject(err); + promise.catch(() => {}); // prevent unhandled rejection testharness.js errors + navigation.onnavigate = e => e.intercept({ handler: () => promise }); + + const result = navigation.navigate("#1"); + + await assertCommittedFulfillsFinishedRejectsExactly(t, result, navigation.currentEntry, err); +}, "navigate() and intercept() with a rejected promise"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-intercept.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-intercept.html new file mode 100644 index 0000000000..a9946c71ff --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-intercept.html @@ -0,0 +1,15 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + navigation.onnavigate = e => e.intercept({ handler: () => Promise.resolve({ abc: 'def' }) }); + + const result = navigation.navigate("#1"); + + await assertBothFulfill(t, result, navigation.currentEntry); + assert_equals((new URL(navigation.currentEntry.url)).hash, "#1"); +}, "navigate() and intercept() with a fulfilled promise"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-interrupted-within-onnavigate.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-interrupted-within-onnavigate.html new file mode 100644 index 0000000000..3db02c6931 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-interrupted-within-onnavigate.html @@ -0,0 +1,28 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + let result2; + navigation.onnavigate = t.step_func(e => { + if (e.info == 1) { + result2 = navigation.navigate("#2", { info: 2 }); + assert_true(e.defaultPrevented); + } + }); + + const result1 = navigation.navigate("#1", { info: 1 }); + + assert_equals(navigation.entries().length, 2); + assert_array_equals(navigation.entries().map(e => (new URL(e.url)).hash), ["", "#2"]); + + await assertBothRejectDOM(t, result1, "AbortError"); + await assertBothFulfill(t, result2, navigation.currentEntry); +}, "if navigate() is called inside onnavigate, the previous navigation and navigate event are cancelled"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-interrupted.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-interrupted.html new file mode 100644 index 0000000000..2c928254e0 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-interrupted.html @@ -0,0 +1,21 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + const result1 = navigation.navigate("#1"); + const result2 = navigation.navigate("#2"); + + assert_equals(navigation.entries().length, 3); + assert_array_equals(navigation.entries().map(e => (new URL(e.url)).hash), ["", "#1", "#2"]); + + await assertCommittedFulfillsFinishedRejectsDOM(t, result1, navigation.entries()[1], "AbortError"); + await assertBothFulfill(t, result2, navigation.currentEntry); +}, "interrupted navigate() promises"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-invalid-url.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-invalid-url.html new file mode 100644 index 0000000000..5b5b442c91 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-invalid-url.html @@ -0,0 +1,10 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + await assertBothRejectDOM(t, navigation.navigate("https://example.com\u0000mozilla.org"), "SyntaxError"); +}, "navigate() with an invalid URL"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-opaque-origin.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-opaque-origin.html new file mode 100644 index 0000000000..51500eb8d8 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-opaque-origin.html @@ -0,0 +1,9 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<iframe id="i" sandbox="allow-scripts" src="resources/navigate-opaque-origin-page.html"></iframe> + +<script> +fetch_tests_from_window(i.contentWindow); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-preventDefault.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-preventDefault.html new file mode 100644 index 0000000000..6257c5a03d --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-preventDefault.html @@ -0,0 +1,12 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + navigation.onnavigate = e => e.preventDefault(); + + await assertBothRejectDOM(t, navigation.navigate("#"), "AbortError"); +}, "navigate() when the onnavigate handler calls preventDefault()"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-push-initial-about-blank.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-push-initial-about-blank.html new file mode 100644 index 0000000000..9d47c8d0b5 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-push-initial-about-blank.html @@ -0,0 +1,21 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<iframe id="i"></iframe> +<script> +promise_test(async t => { + // Wait for after the load event so that we are definitely testing the initial + // about:blank-ness as the cause of the rejections. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + i.contentWindow.navigation.onnavigate = t.unreached_func("onnavigate should not be called"); + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + const result = i.contentWindow.navigation.navigate("#1", { history: "push" }); + await assertBothRejectDOM(t, result, "NotSupportedError", i.contentWindow); +}, "navigate() with history: 'push' in initial about:blank document"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-push-javascript-url.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-push-javascript-url.html new file mode 100644 index 0000000000..e41e06a78d --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-push-javascript-url.html @@ -0,0 +1,19 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that we are definitely testing the + // javascript: URL as the cause of the rejections. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + navigation.onnavigate = t.unreached_func("onnavigate should not be called"); + navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + const result = navigation.navigate("javascript:'foo'", { history: "push" }); + await assertBothRejectDOM(t, result, "NotSupportedError"); +}, "navigate() to a javascript: URL"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-beforeunload-unserializablestate.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-beforeunload-unserializablestate.html new file mode 100644 index 0000000000..878280a56e --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-beforeunload-unserializablestate.html @@ -0,0 +1,31 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + let navigateEventCount = 0; + i.contentWindow.navigation.onnavigate = () => navigateEventCount++; + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func('onnavigatesuccess should not be called'); + i.contentWindow.navigation.onnavigateerror = t.unreached_func('onnavigateerror should not be called'); + + let assertionPromise; + // The iframe does not have sticky activation, so per + // https://html.spec.whatwg.org/#prompt-to-unload-a-document, no prompt is + // shown and the navigation will proceed. + i.contentWindow.onbeforeunload = t.step_func(() => { + assertionPromise = assertBothRejectDOM(t, i.contentWindow.navigation.navigate("/common/blank.html?1", { state: document.body }), "DataCloneError", i.contentWindow); + }); + i.contentWindow.navigation.navigate("?1"); + + assert_not_equals(assertionPromise, undefined); + await assertionPromise; + + assert_equals(navigateEventCount, 1); +}, `navigate() with an unserializable state inside onbeforeunload "DataCloneError", not "InvalidStateError"`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-detached-unserializablestate.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-detached-unserializablestate.html new file mode 100644 index 0000000000..2c6f9a28d4 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-detached-unserializablestate.html @@ -0,0 +1,20 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + + i.remove(); + + iWindow.navigation.onnavigate = t.unreached_func("onnavigate"); + + await assertBothRejectDOM(t, iWindow.navigation.navigate("https://example.com/", { state: document.body }), "DataCloneError", iWindow, iDOMException); +}, `navigate() with unserializable state in a detached iframe throws "DataCloneError", not "InvalidStateError"`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-beforeunload.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-beforeunload.html new file mode 100644 index 0000000000..4873e85a2f --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-beforeunload.html @@ -0,0 +1,31 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + let navigateEventCount = 0; + i.contentWindow.navigation.onnavigate = () => navigateEventCount++; + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + let assertionPromise; + // The iframe does not have sticky activation, so per + // https://html.spec.whatwg.org/#prompt-to-unload-a-document, no prompt is + // shown and the navigation will proceed. + i.contentWindow.onbeforeunload = t.step_func(() => { + assertionPromise = assertBothRejectDOM(t, i.contentWindow.navigation.navigate("https://example.com\u0000mozilla.org"), "SyntaxError", i.contentWindow); + }); + i.contentWindow.navigation.navigate("?1"); + + assert_not_equals(assertionPromise, undefined); + await assertionPromise; + + assert_equals(navigateEventCount, 1); +}, `navigate() with an invalid URL inside onbeforeunload throws "SyntaxError", not "InvalidStateError"`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-detached.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-detached.html new file mode 100644 index 0000000000..2250a54114 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-detached.html @@ -0,0 +1,20 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + + i.remove(); + + iWindow.navigation.onnavigate = t.unreached_func("onnavigate"); + + await assertBothRejectDOM(t, iWindow.navigation.navigate("https://example.com\u0000mozilla.org"), "SyntaxError", iWindow, iDOMException); +}, `navigate() with an invalid URL in a detached iframe throws "SyntaxError", not "InvalidStateError"`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-unload.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-unload.html new file mode 100644 index 0000000000..d778dd662c --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-unload.html @@ -0,0 +1,27 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + let navigateEventCount = 0; + i.contentWindow.navigation.onnavigate = () => navigateEventCount++; + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + i.contentWindow.navigation.navigate("?1"); + + await new Promise(resolve => { + i.contentWindow.onunload = t.step_func(async () => { + await assertBothRejectDOM(t, i.contentWindow.navigation.navigate("https://example.com\u0000mozilla.org"), "SyntaxError", i.contentWindow); + assert_equals(navigateEventCount, 1); + resolve(); + }); + }); +}, `navigate() with an invalid URL inside onunload throws "SyntaxError", not "InvalidStateError"`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-unserializablestate.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-unserializablestate.html new file mode 100644 index 0000000000..07e194ca41 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-unserializablestate.html @@ -0,0 +1,12 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + navigation.onnavigate = t.unreached_func("onnavigate"); + + await assertBothRejectDOM(t, navigation.navigate("https://example.com\u0000mozilla.org", { state: document.body }), "SyntaxError"); +}, `navigate() with an invalid URL and unserializable state throws "SyntaxError", not "DataCloneError"`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-unload-unserializablestate.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-unload-unserializablestate.html new file mode 100644 index 0000000000..417dd27174 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-rejection-order-unload-unserializablestate.html @@ -0,0 +1,27 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + let navigateEventCount = 0; + i.contentWindow.navigation.onnavigate = () => navigateEventCount++; + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + i.contentWindow.navigation.navigate("?1"); + + await new Promise(resolve => { + i.contentWindow.onunload = t.step_func(async () => { + await assertBothRejectDOM(t, i.contentWindow.navigation.navigate("?2", { state: document.body }), "DataCloneError", i.contentWindow); + assert_equals(navigateEventCount, 1); + resolve(); + }); + }); +}, `navigate() with an unserializable state inside onunload throws "DataCloneError", not "InvalidStateError"`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-unload.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-unload.html new file mode 100644 index 0000000000..fbc1fde6e9 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-unload.html @@ -0,0 +1,27 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + let navigateEventCount = 0; + i.contentWindow.navigation.onnavigate = () => navigateEventCount++; + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + i.contentWindow.navigation.navigate("?1"); + + await new Promise(resolve => { + i.contentWindow.onunload = t.step_func(async () => { + await assertBothRejectDOM(t, i.contentWindow.navigation.navigate("?2"), "InvalidStateError", i.contentWindow); + assert_equals(navigateEventCount, 1); + resolve(); + }); + }); +}, `navigate() inside onunload`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-unserializable-state.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-unserializable-state.html new file mode 100644 index 0000000000..36464ec3c5 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate-unserializable-state.html @@ -0,0 +1,21 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + await assertBothRejectDOM(t, navigation.navigate("#1", { state: new WritableStream() }), "DataCloneError"); + assert_equals(navigation.currentEntry.getState(), undefined); + assert_equals(location.hash, ""); +}, "navigate() with an unserializable state (WritableStream)"); + +promise_test(async t => { + // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()` + const buffer = new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer; + + await assertBothRejectDOM(t, navigation.navigate("#2", { state: buffer }), "DataCloneError"); + assert_equals(navigation.currentEntry.getState(), undefined); + assert_equals(location.hash, ""); +}, "navigate() with an unserializable state (SharedArrayBuffer)"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate.html new file mode 100644 index 0000000000..34ff84f0e9 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/navigate.html @@ -0,0 +1,12 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + const result = navigation.navigate("#1"); + await assertBothFulfill(t, result, navigation.currentEntry); + assert_equals((new URL(navigation.currentEntry.url)).hash, "#1"); +}, "navigate() promises"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-already-detached.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-already-detached.html new file mode 100644 index 0000000000..9b0780c4a9 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-already-detached.html @@ -0,0 +1,19 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + + i.remove(); + + await assertBothRejectDOM(t, iWindow.navigation.reload(), "InvalidStateError", iWindow, iDOMException); +}, "reload() in a detached window"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-beforeunload.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-beforeunload.html new file mode 100644 index 0000000000..05338acc5e --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-beforeunload.html @@ -0,0 +1,31 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + let navigateEventCount = 0; + i.contentWindow.navigation.onnavigate = () => navigateEventCount++; + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + let assertionPromise; + // The iframe does not have sticky activation, so per + // https://html.spec.whatwg.org/#prompt-to-unload-a-document, no prompt is + // shown and the navigation will proceed. + i.contentWindow.onbeforeunload = t.step_func(() => { + assertionPromise = assertBothRejectDOM(t, i.contentWindow.navigation.reload(), "InvalidStateError", i.contentWindow); + }); + i.contentWindow.navigation.navigate("?1"); + + assert_not_equals(assertionPromise, undefined); + await assertionPromise; + + assert_equals(navigateEventCount, 1); +}, "reload() inside onbeforeunload"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-detach-in-onnavigate.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-detach-in-onnavigate.html new file mode 100644 index 0000000000..621bb598f6 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-detach-in-onnavigate.html @@ -0,0 +1,18 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + i.contentWindow.navigation.onnavigate = () => i.remove(); + + await assertBothRejectDOM(t, i.contentWindow.navigation.reload(), "AbortError", iWindow, iDOMException); +}, "reload() promise rejections when detaching an iframe inside onnavigate"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-detach-in-serialization.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-detach-in-serialization.html new file mode 100644 index 0000000000..d958d9cc84 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-detach-in-serialization.html @@ -0,0 +1,24 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + + const trappedState = { + get prop() { + i.remove(); + return "whatever"; + } + }; + + await assertBothRejectDOM(t, i.contentWindow.navigation.reload({ state: trappedState }), "InvalidStateError", iWindow, iDOMException); +}, "reload() promise rejections when detaching an iframe inside state serialization"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-initial-about-blank.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-initial-about-blank.html new file mode 100644 index 0000000000..9e717fee48 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-initial-about-blank.html @@ -0,0 +1,20 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<body> +<script> +promise_test(async t => { + let i = document.createElement("iframe"); + document.body.append(i); + + i.contentWindow.navigation.onnavigate = t.unreached_func("onnavigate should not be called"); + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + const result = i.contentWindow.navigation.reload(); + assertNeverSettles(t, result, i.contentWindow); + await new Promise(resolve => t.step_timeout(resolve, 10)); +}, "reload() in initial about:blank document"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-intercept-rejected.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-intercept-rejected.html new file mode 100644 index 0000000000..93cd75c2b9 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-intercept-rejected.html @@ -0,0 +1,17 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + const err = new Error("boo!"); + const promise = Promise.reject(err); + promise.catch(() => {}); // prevent unhandled rejection testharness.js errors + navigation.onnavigate = e => e.intercept({ handler: () => promise }); + + const result = navigation.reload(); + + await assertCommittedFulfillsFinishedRejectsExactly(t, result, navigation.currentEntry, err); +}, "reload() and intercept() with a rejected promise"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-intercept.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-intercept.html new file mode 100644 index 0000000000..92f1636a6e --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-intercept.html @@ -0,0 +1,14 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + navigation.onnavigate = e => e.intercept({ handler: () => Promise.resolve({ abc: 'def' }) }); + + const result = navigation.reload(); + + await assertBothFulfill(t, result, navigation.currentEntry); +}, "reload() and intercept() with a fulfilled promise"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-preventDefault.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-preventDefault.html new file mode 100644 index 0000000000..747044b60f --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-preventDefault.html @@ -0,0 +1,12 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + navigation.onnavigate = e => e.preventDefault(); + + await assertBothRejectDOM(t, navigation.reload(), "AbortError"); +}, "reload() when the onnavigate handler calls preventDefault()"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-rejection-order-beforeunload-unserializablestate.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-rejection-order-beforeunload-unserializablestate.html new file mode 100644 index 0000000000..950d665218 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-rejection-order-beforeunload-unserializablestate.html @@ -0,0 +1,31 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + let navigateEventCount = 0; + i.contentWindow.navigation.onnavigate = () => navigateEventCount++; + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func('onnavigatesuccess should not be called'); + i.contentWindow.navigation.onnavigateerror = t.unreached_func('onnavigateerror should not be called'); + + let assertionPromise; + // The iframe does not have sticky activation, so per + // https://html.spec.whatwg.org/#prompt-to-unload-a-document, no prompt is + // shown and the navigation will proceed. + i.contentWindow.onbeforeunload = t.step_func(() => { + assertionPromise = assertBothRejectDOM(t, i.contentWindow.navigation.reload({ state: document.body }), "DataCloneError", i.contentWindow); + }); + i.contentWindow.navigation.navigate("?1"); + + assert_not_equals(assertionPromise, undefined); + await assertionPromise; + + assert_equals(navigateEventCount, 1); +}, `reload() with an unserializable state inside onbeforeunload throws "DataCloneError", not "InvalidStateError"`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-rejection-order-detached-unserializablestate.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-rejection-order-detached-unserializablestate.html new file mode 100644 index 0000000000..9206f18e6c --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-rejection-order-detached-unserializablestate.html @@ -0,0 +1,20 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + + i.remove(); + + iWindow.navigation.onnavigate = t.unreached_func("onnavigate"); + + await assertBothRejectDOM(t, iWindow.navigation.reload({ state: document.body }), "DataCloneError", iWindow, iDOMException); +}, `reload() with unserializable state in a detached iframe throws "DataCloneError", not "InvalidStateError"`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-rejection-order-unload-unserializablestate.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-rejection-order-unload-unserializablestate.html new file mode 100644 index 0000000000..9e7edcbba4 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-rejection-order-unload-unserializablestate.html @@ -0,0 +1,32 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + let navigateEventCount = 0; + i.contentWindow.navigation.onnavigate = () => navigateEventCount++; + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + i.contentWindow.navigation.navigate("?1"); + + let assertionPromise; + await new Promise(resolve => { + i.contentWindow.onunload = t.step_func(() => { + assertionPromise = assertBothRejectDOM(t, i.contentWindow.navigation.reload({ state: document.body }), "DataCloneError", i.contentWindow); + assert_equals(navigateEventCount, 1); + resolve(); + }); + }); + + assert_not_equals(assertionPromise, undefined); + await assertionPromise; + +}, `reload() with an unserializable state inside onunload throws "DataCloneError", not "InvalidStateError"`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-unload.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-unload.html new file mode 100644 index 0000000000..1906659d1d --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-unload.html @@ -0,0 +1,27 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + let navigateEventCount = 0; + i.contentWindow.navigation.onnavigate = () => navigateEventCount++; + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + i.contentWindow.navigation.navigate("?1"); + + await new Promise(resolve => { + i.contentWindow.onunload = t.step_func(async () => { + await assertBothRejectDOM(t, i.contentWindow.navigation.reload(), "InvalidStateError", i.contentWindow); + assert_equals(navigateEventCount, 1); + resolve(); + }); + }); +}, `reload() inside onunload`); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-unserializable-state.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-unserializable-state.html new file mode 100644 index 0000000000..76fa870558 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload-unserializable-state.html @@ -0,0 +1,21 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + await assertBothRejectDOM(t, navigation.reload({ state: new WritableStream() }), "DataCloneError"); + assert_equals(navigation.currentEntry.getState(), undefined); + assert_equals(location.hash, ""); +}, "reload() with an unserializable state (WritableStream)"); + +promise_test(async t => { + // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()` + const buffer = new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer; + + await assertBothRejectDOM(t, navigation.reload({ state: buffer }), "DataCloneError"); + assert_equals(navigation.currentEntry.getState(), undefined); + assert_equals(location.hash, ""); +}, "reload() with an unserializable state (SharedArrayBuffer)"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload.html new file mode 100644 index 0000000000..388f0d94b5 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/reload.html @@ -0,0 +1,16 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + const result = i.contentWindow.navigation.reload(); + assertNeverSettles(t, result, i.contentWindow); + + await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0)); +}, "reload() promises never settle (without intercept())"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/resources/204-205-download-on-second-visit.py b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/resources/204-205-download-on-second-visit.py new file mode 100644 index 0000000000..c18b0dec3d --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/resources/204-205-download-on-second-visit.py @@ -0,0 +1,24 @@ +def main(request, response): + key = request.GET[b"id"] + + # If hit with a POST with ?action=X, store X in the stash + if request.method == "POST": + action = request.GET[b"action"] + request.server.stash.put(key, action) + + return (204, [], "") + + # If hit with a GET, either return a normal initial page, or the abnormal requested response + elif request.method == "GET": + action = request.server.stash.take(key) + + if action is None: + return (200, [("Content-Type", "text/html"), ("Cache-Control", "no-store")], "initial page") + if action == b"204": + return (204, [], "") + if action == b"205": + return (205, [], "") + if action == b"download": + return (200, [("Content-Type", "text/plain"), ("Content-Disposition", "attachment")], "some text to download") + + return (400, [], "") diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/resources/back-forward-opaque-origin-page.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/resources/back-forward-opaque-origin-page.html new file mode 100644 index 0000000000..ec63363952 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/resources/back-forward-opaque-origin-page.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="helpers.js"></script> +<!-- Put this page in a sandbox to give it an opaque origin --> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + navigation.onnavigate = t.unreached_func("onnavigate should not be called"); + navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + location.hash = "#1"; + await new Promise(resolve => window.onhashchange = resolve); + location.hash = "#2"; + await new Promise(resolve => window.onhashchange = resolve); + history.back(); + await new Promise(resolve => window.onhashchange = resolve); + + assert_equals(location.hash, "#1"); + + await assertBothRejectDOM(t, navigation.back(), "InvalidStateError"); + await assertBothRejectDOM(t, navigation.forward(), "InvalidStateError"); +}, "navigation.back()/forward() in an opaque origin iframe"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/resources/helpers.js b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/resources/helpers.js new file mode 100644 index 0000000000..63d706ed28 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/resources/helpers.js @@ -0,0 +1,127 @@ +window.assertReturnValue = (result, w = window) => { + assert_equals(Object.getPrototypeOf(result), w.Object.prototype, "result object must be from the right realm"); + assert_array_equals(Reflect.ownKeys(result), ["committed", "finished"]); + assert_true(result.committed instanceof w.Promise); + assert_true(result.finished instanceof w.Promise); + assert_not_equals(result.committed, result.finished); +}; + +window.assertNeverSettles = (t, result, w = window) => { + assertReturnValue(result, w); + result.committed.then( + t.unreached_func("committed must not fulfill"), + t.unreached_func("committed must not reject") + ); + + result.finished.then( + t.unreached_func("finished must not fulfill"), + t.unreached_func("finished must not reject") + ); +}; + +window.assertBothFulfillEntryNotAvailable = async (t, result, w = window) => { + assertReturnValue(result, w); + + // Don't use await here so that we can catch out-of-order settlements. + let committedValue; + result.committed.then( + t.step_func(v => { committedValue = v;}), + t.unreached_func("committed must not reject") + ); + + const finishedValue = await result.finished; + + assert_not_equals(committedValue, undefined, "committed must fulfill before finished"); + assert_equals(finishedValue, committedValue, "committed and finished must fulfill with the same value"); + assert_true(finishedValue instanceof w.NavigationHistoryEntry, "fulfillment value must be a NavigationHistoryEntry"); +}; + +window.assertBothFulfill = async (t, result, expected, w = window) => { + assertReturnValue(result, w); + + // Don't use await here so that we can catch out-of-order settlements. + let committedValue; + result.committed.then( + t.step_func(v => { committedValue = v; }), + t.unreached_func("committed must not reject") + ); + + const finishedValue = await result.finished; + + assert_not_equals(committedValue, undefined, "committed must fulfill before finished"); + assert_equals(finishedValue, committedValue, "committed and finished must fulfill with the same value"); + assert_true(finishedValue instanceof w.NavigationHistoryEntry, "fulfillment value must be a NavigationHistoryEntry"); + assert_equals(finishedValue, expected); +}; + +window.assertCommittedFulfillsFinishedRejectsExactly = async (t, result, expectedEntry, expectedRejection, w = window) => { + assertReturnValue(result, w); + + // Don't use await here so that we can catch out-of-order settlements. + let committedValue; + result.committed.then( + t.step_func(v => { committedValue = v; }), + t.unreached_func("committed must not reject") + ); + + await promise_rejects_exactly(t, expectedRejection, result.finished); + + assert_not_equals(committedValue, undefined, "committed must fulfill before finished rejects"); + assert_true(committedValue instanceof w.NavigationHistoryEntry, "fulfillment value must be a NavigationHistoryEntry"); + assert_equals(committedValue, expectedEntry); +}; + +window.assertCommittedFulfillsFinishedRejectsDOM = async (t, result, expectedEntry, expectedDOMExceptionCode, w = window, domExceptionConstructor = w.DOMException, navigationHistoryEntryConstuctor = w.NavigationHistoryEntry) => { + assertReturnValue(result, w); + + let committedValue; + result.committed.then( + t.step_func(v => { committedValue = v; }), + t.unreached_func("committed must not reject") + ); + + await promise_rejects_dom(t, expectedDOMExceptionCode, domExceptionConstructor, result.finished); + + assert_not_equals(committedValue, undefined, "committed must fulfill before finished rejects"); + assert_true(committedValue instanceof navigationHistoryEntryConstuctor, "fulfillment value must be an NavigationHistoryEntry"); + assert_equals(committedValue, expectedEntry); +}; + +window.assertBothRejectExactly = async (t, result, expectedRejection, w = window) => { + assertReturnValue(result, w); + + let committedReason, finishedReason; + await Promise.all([ + result.committed.then( + t.unreached_func("committed must not fulfill"), + t.step_func(r => { committedReason = r; }) + ), + result.finished.then( + t.unreached_func("finished must not fulfill"), + t.step_func(r => { finishedReason = r; }) + ) + ]); + + assert_equals(committedReason, finishedReason, "committed and finished must reject with the same value"); + assert_equals(expectedRejection, committedReason); +}; + +window.assertBothRejectDOM = async (t, result, expectedDOMExceptionCode, w = window, domExceptionConstructor = w.DOMException) => { + assertReturnValue(result, w); + + // Don't use await here so that we can catch out-of-order settlements. + let committedReason, finishedReason; + await Promise.all([ + result.committed.then( + t.unreached_func("committed must not fulfill"), + t.step_func(r => { committedReason = r; }) + ), + result.finished.then( + t.unreached_func("finished must not fulfill"), + t.step_func(r => { finishedReason = r; }) + ) + ]); + + assert_equals(committedReason, finishedReason, "committed and finished must reject with the same value"); + assert_throws_dom(expectedDOMExceptionCode, domExceptionConstructor, () => { throw committedReason; }); +}; diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/resources/navigate-opaque-origin-page.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/resources/navigate-opaque-origin-page.html new file mode 100644 index 0000000000..831eefdb61 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/resources/navigate-opaque-origin-page.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="helpers.js"></script> +<!-- Put this page in a sandbox to give it an opaque origin --> + +<script> +promise_test(async t => { + navigation.onnavigate = t.unreached_func("onnavigate should not be called"); + navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + const result = navigation.navigate("#1"); + assertNeverSettles(t, result); + await new Promise(resolve => t.step_timeout(resolve, 10)); +}, "navigation.navigate() in an opaque origin iframe"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-already-detached.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-already-detached.html new file mode 100644 index 0000000000..b974393df6 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-already-detached.html @@ -0,0 +1,20 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + const key = iWindow.navigation.currentEntry.key; + + i.remove(); + + await assertBothRejectDOM(t, iWindow.navigation.traverseTo(key), "InvalidStateError", iWindow, iDOMException); +}, "traverseTo() in a detached window"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-beforeunload.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-beforeunload.html new file mode 100644 index 0000000000..3b2722235e --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-beforeunload.html @@ -0,0 +1,31 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + let navigateEventCount = 0; + i.contentWindow.navigation.onnavigate = () => navigateEventCount++; + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess should not be called"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called"); + + let assertionPromise; + // The iframe does not have sticky activation, so per + // https://html.spec.whatwg.org/#prompt-to-unload-a-document, no prompt is + // shown and the navigation will proceed. + i.contentWindow.onbeforeunload = t.step_func(() => { + assertionPromise = assertBothRejectDOM(t, i.contentWindow.navigation.traverseTo(i.contentWindow.navigation.currentEntry.key), "InvalidStateError", i.contentWindow); + }); + i.contentWindow.navigation.navigate("?1"); + + assert_not_equals(assertionPromise, undefined); + await assertionPromise; + + assert_equals(navigateEventCount, 1); +}, "traverseTo() inside onbeforeunload"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-cross-document-preventDefault.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-cross-document-preventDefault.html new file mode 100644 index 0000000000..09c91ee647 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-cross-document-preventDefault.html @@ -0,0 +1,29 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + assert_equals(i.contentWindow.navigation.entries().length, 1); + let key = i.contentWindow.navigation.currentEntry.key; + + i.contentWindow.navigation.navigate("?1"); + await new Promise(resolve => i.onload = resolve); + + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry, i.contentWindow.navigation.entries()[1]); + + // This will be a noop, because navigate events are uncancelable for traversals. + i.contentWindow.navigation.onnavigate = e => e.preventDefault(); + + assertNeverSettles(t, i.contentWindow.navigation.traverseTo(key), i.contentWindow); + await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0)); +}, "traverseTo() promise never settle when preventDefault()ing the navigate event (cross-document)"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-current.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-current.html new file mode 100644 index 0000000000..212fe992cf --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-current.html @@ -0,0 +1,17 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + assert_equals(navigation.entries().length, 1); + const entry = navigation.currentEntry; + + const result = navigation.traverseTo(navigation.currentEntry.key); + await assertBothFulfill(t, result, entry); + + assert_equals(navigation.entries().length, 1); + assert_equals(navigation.currentEntry, entry); +}, "traverseTo() with current key"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-detach-cross-document-before-navigate-event.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-detach-cross-document-before-navigate-event.html new file mode 100644 index 0000000000..ed319f6223 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-detach-cross-document-before-navigate-event.html @@ -0,0 +1,32 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + assert_equals(i.contentWindow.navigation.entries().length, 1); + let key = i.contentWindow.navigation.currentEntry.key; + + i.contentWindow.navigation.navigate("?1"); + await new Promise(resolve => i.onload = resolve); + + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry, i.contentWindow.navigation.entries()[1]); + + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + i.contentWindow.navigation.onnavigate = t.unreached_func("navigate should not fire"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("navigateerror should not fire"); + + let promises = i.contentWindow.navigation.traverseTo(key); + i.remove(); + await assertBothRejectDOM(t, promises, "AbortError", iWindow, iDOMException); +}, "traverseTo() promise rejections when detaching an iframe before onnavigate (cross-document)"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-detach-cross-document.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-detach-cross-document.html new file mode 100644 index 0000000000..8784313b70 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-detach-cross-document.html @@ -0,0 +1,29 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + assert_equals(i.contentWindow.navigation.entries().length, 1); + let key = i.contentWindow.navigation.currentEntry.key; + + i.contentWindow.navigation.navigate("?1"); + await new Promise(resolve => i.onload = resolve); + + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry, i.contentWindow.navigation.entries()[1]); + + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + i.contentWindow.navigation.onnavigate = () => i.remove(); + + await assertBothRejectDOM(t, i.contentWindow.navigation.traverseTo(key), "AbortError", iWindow, iDOMException); +}, "traverseTo() promise rejections when detaching an iframe inside onnavigate (cross-document)"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-detach-same-document-before-navigate-event.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-detach-same-document-before-navigate-event.html new file mode 100644 index 0000000000..592ac6f199 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-detach-same-document-before-navigate-event.html @@ -0,0 +1,31 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + assert_equals(i.contentWindow.navigation.entries().length, 1); + let key = i.contentWindow.navigation.currentEntry.key; + + await i.contentWindow.navigation.navigate("#1").committed; + + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry, i.contentWindow.navigation.entries()[1]); + + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + i.contentWindow.navigation.onnavigate = t.unreached_func("navigate should not fire"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("navigateerror should not fire"); + + let promises = i.contentWindow.navigation.traverseTo(key); + i.remove(); + await assertBothRejectDOM(t, promises, "AbortError", iWindow, iDOMException); +}, "traverseTo() promise rejections when detaching an iframe before onnavigate (same-document)"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-detach-same-document.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-detach-same-document.html new file mode 100644 index 0000000000..b0308b8df8 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-detach-same-document.html @@ -0,0 +1,28 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + assert_equals(i.contentWindow.navigation.entries().length, 1); + let key = i.contentWindow.navigation.currentEntry.key; + + await i.contentWindow.navigation.navigate("#1").committed; + + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry, i.contentWindow.navigation.entries()[1]); + + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + i.contentWindow.navigation.onnavigate = () => i.remove(); + + await assertBothRejectDOM(t, i.contentWindow.navigation.traverseTo(key), "AbortError", iWindow, iDOMException); +}, "traverseTo() promise rejections when detaching an iframe inside onnavigate (same-document)"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-intercept-rejected.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-intercept-rejected.html new file mode 100644 index 0000000000..f934eae5d4 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-intercept-rejected.html @@ -0,0 +1,30 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + const key0 = navigation.currentEntry.key; + + location.href = "#1"; + + assert_equals(navigation.entries().length, 2); + const [entry0, entry1] = navigation.entries(); + assert_equals((new URL(entry0.url)).hash, ""); + assert_equals((new URL(entry1.url)).hash, "#1"); + + const err = new Error("boo!"); + const promise = Promise.reject(err); + promise.catch(() => {}); // prevent unhandled rejection testharness.js errors + navigation.onnavigate = e => e.intercept({ handler: () => promise }); + + const result = navigation.traverseTo(key0); + await assertCommittedFulfillsFinishedRejectsExactly(t, result, entry0, err); + assert_equals(navigation.currentEntry, entry0); +}, "traverseTo() promise rejection with rejected intercept()"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-intercept.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-intercept.html new file mode 100644 index 0000000000..a4cb4e711d --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-intercept.html @@ -0,0 +1,27 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + const key0 = navigation.currentEntry.key; + + location.href = "#1"; + + assert_equals(navigation.entries().length, 2); + const [entry0, entry1] = navigation.entries(); + assert_equals((new URL(entry0.url)).hash, ""); + assert_equals((new URL(entry1.url)).hash, "#1"); + + navigation.onnavigate = e => e.intercept({ handler: () => Promise.resolve({ abc: "def" }) }); + + const result = navigation.traverseTo(key0); + await assertBothFulfill(t, result, entry0); + assert_equals(navigation.currentEntry, entry0); +}, "traverseTo() and intercept() with a fulfilled promise"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-invalid-key.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-invalid-key.html new file mode 100644 index 0000000000..42be40bfa7 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-invalid-key.html @@ -0,0 +1,10 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + await assertBothRejectDOM(t, navigation.traverseTo("not a real key"), "InvalidStateError"); +}, "traverseTo() with invalid key"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-repeated.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-repeated.html new file mode 100644 index 0000000000..d1754d6729 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo-repeated.html @@ -0,0 +1,24 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + const key = navigation.currentEntry.key; + const entry = navigation.currentEntry; + await navigation.navigate("#1").committed; + + const result1 = navigation.traverseTo(key); + const result2 = navigation.traverseTo(key); + + await assertBothFulfill(t, result1, entry); + assert_not_equals(result1, result2); + assert_equals(result2.committed, result1.committed, "committed promises must be equal"); + assert_equals(result2.finished, result1.finished, "finished promises must be equal"); +}, "Repeated navigation.traverseTo() with the same key"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo.html b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo.html new file mode 100644 index 0000000000..8e00f5ba91 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/return-value/traverseTo.html @@ -0,0 +1,25 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/helpers.js"></script> + +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + const key0 = navigation.currentEntry.key; + + location.href = "#1"; + + assert_equals(navigation.entries().length, 2); + const [entry0, entry1] = navigation.entries(); + assert_equals((new URL(entry0.url)).hash, ""); + assert_equals((new URL(entry1.url)).hash, "#1"); + + const result = navigation.traverseTo(key0); + await assertBothFulfill(t, result, entry0); + assert_equals(navigation.currentEntry, entry0); +}, "traverseTo() promises"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/sandboxing-back-parent.html b/testing/web-platform/tests/navigation-api/navigation-methods/sandboxing-back-parent.html new file mode 100644 index 0000000000..6eee3f917c --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/sandboxing-back-parent.html @@ -0,0 +1,38 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="return-value/resources/helpers.js"></script> +<iframe id="i" src="/common/blank.html?startI" sandbox="allow-scripts allow-same-origin"></iframe> + +<script> +// Intended setup: +// Step 0: +// - Parent: (current URL) +// - i: /common/blank.html?startI +// Step 1: +// - Parent: (current URL) +// - i: resources/navigation-back.html +// Step 2: +// - Parent: (current URL)#end +// - i: resources/navigation-back.html +// +// Then, calling navigation.back() in i will take is from step 2 to step 0, which would navigate the parent. +// That is not allowed, so the call to back() must reject. + +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + i.contentWindow.location.href = new URL("resources/navigation-back.html", location.href); + await new Promise(resolve => i.onload = resolve); + + location.hash = "#end"; + await new Promise(resolve => window.onhashchange = resolve); + + navigation.onnavigate = t.unreached_func("navigate must not fire"); + navigation.onnavigateerror = t.unreached_func("navigateerror must not fire"); + window.onpopstate = t.unreached_func("popstate must not fire"); + window.onhashchange = t.unreached_func("hashchange must not fire"); + + await assertBothRejectDOM(t, i.contentWindow.doNavigationBack(), "SecurityError", i.contentWindow); +}, "A sandboxed iframe cannot navigate its parent via its own navigation object by using back()"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/sandboxing-back-sibling.html b/testing/web-platform/tests/navigation-api/navigation-methods/sandboxing-back-sibling.html new file mode 100644 index 0000000000..97c03ea0c2 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/sandboxing-back-sibling.html @@ -0,0 +1,44 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="return-value/resources/helpers.js"></script> +<iframe id="i" src="/common/blank.html?startI" sandbox="allow-same-origin"></iframe> +<iframe id="i2" src="/common/blank.html?startI2" sandbox="allow-scripts allow-same-origin"></iframe> + +<script> +// Intended setup: +// Step 0: +// - Parent: (current URL) +// - i: /common/blank.html?startI +// - i2: /common/blank.html?startI2 +// Step 1: +// - Parent: (current URL) +// - i: /common/blank.html?startI +// - i2: resources/navigation-back.html +// Step 2: +// - Parent: (current URL) +// - i: /common/blank.html?endI +// - i2: resources/navigation-back.html +// +// Then, calling navigation.back() in i2 will take is from step 2 to step 0, which would navigate i. +// That is not allowed, so the call to back() must reject. + +promise_test(async t => { + await new Promise(resolve => window.onload = resolve); + + i2.contentWindow.location.href = new URL("resources/navigation-back.html", location.href); + await new Promise(resolve => i2.onload = resolve); + + i.contentWindow.location.href = "/common/blank.html?endI"; + await new Promise(resolve => i.onload = resolve); + + i.contentWindow.navigation.onnavigate = t.unreached_func("navigate must not fire"); + i.contentWindow.navigation.onnavigateerror = t.unreached_func("navigateerror must not fire"); + i.contentWindow.onbeforeunload = t.unreached_func("beforeunload must not fire"); + i.contentWindow.onunload = t.unreached_func("unload must not fire"); + i.contentWindow.onpagehide = t.unreached_func("pagehide must not fire"); + i.contentWindow.onpopstate = t.unreached_func("popstate must not fire"); + + await assertBothRejectDOM(t, i2.contentWindow.doNavigationBack(), "SecurityError", i2.contentWindow); +}, "A sandboxed iframe cannot navigate its sibling via its own navigation object by using back()"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/sandboxing-navigate-parent.html b/testing/web-platform/tests/navigation-api/navigation-methods/sandboxing-navigate-parent.html new file mode 100644 index 0000000000..b7af32443f --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/sandboxing-navigate-parent.html @@ -0,0 +1,16 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="resources/navigate-parent.html" sandbox="allow-scripts allow-same-origin"></iframe> + +<script> +async_test(t => { + window.onload = t.step_func_done(() => { + i.contentWindow.navigateParent(); + + const destinationURL = (new URL("#2", location.href)).href; + assert_equals(location.href, destinationURL); + assert_equals(navigation.currentEntry.url, destinationURL); + }); +}, "A sandboxed iframe can use its sibling's navigation object to call navigate(), as long as allow-same-origin is present"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/sandboxing-navigate-sibling.html b/testing/web-platform/tests/navigation-api/navigation-methods/sandboxing-navigate-sibling.html new file mode 100644 index 0000000000..c8e76fc4a6 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/sandboxing-navigate-sibling.html @@ -0,0 +1,19 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html" sandbox="allow-same-origin"></iframe> +<iframe id="i2" src="resources/navigate-sibling.html" sandbox="allow-scripts allow-same-origin"></iframe> + +<script> +async_test(t => { + window.onload = t.step_func(() => { + i2.contentWindow.navigateSibling(); + + i.onload = t.step_func_done(() => { + const destinationURL = (new URL("/common/blank.html?2", location.href)).href; + assert_equals(i.contentWindow.location.href, destinationURL); + assert_equals(i.contentWindow.navigation.currentEntry.url, destinationURL); + }); + }); +}, "A sandboxed iframe can use its parent's navigation object to call navigate(), as long as allow-same-origin is present"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-after-adding-iframe.html b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-after-adding-iframe.html new file mode 100644 index 0000000000..5ab2820551 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-after-adding-iframe.html @@ -0,0 +1,34 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> +async_test(t => { + let start_length = history.length; + + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + window.onload = () => t.step_timeout(() => { + navigation.navigate("#top"); + assert_equals(navigation.entries().length, 2); + + let i = document.createElement("iframe"); + i.src = "/common/blank.html"; + document.body.appendChild(i); + i.onload = () => t.step_timeout(() => { + assert_equals(i.contentWindow.navigation.entries().length, 1); + i.contentWindow.navigation.navigate("#fragment"); + assert_equals(i.contentWindow.navigation.entries().length, 2); + i.contentWindow.navigation.back().committed.then(t.step_func(() => { + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0); + return i.contentWindow.navigation.forward().committed; + })).then(t.step_func_done(() => { + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1); + })); + }, 0); + }, 0); +}, "navigation.traverseTo() should work in an iframe that is not present in all history entries"); +</script> +</body> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-after-data-url.html b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-after-data-url.html new file mode 100644 index 0000000000..0b21a741d3 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-after-data-url.html @@ -0,0 +1,33 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> +async_test(t => { + let i = document.createElement("iframe"); + i.src = "data:text/html,"; + document.body.appendChild(i); + let start_length = history.length; + + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + window.onload = () => t.step_timeout(() => { + i.contentWindow.location = "/common/blank.html"; + i.onload = () => t.step_timeout(() => { + assert_equals(history.length, start_length + 1); + assert_equals(i.contentWindow.navigation.entries().length, 1); + i.contentWindow.navigation.navigate("#fragment"); + assert_equals(i.contentWindow.navigation.entries().length, 2); + i.contentWindow.navigation.back().committed.then(t.step_func(() => { + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0); + return i.contentWindow.navigation.forward().committed; + })).then(t.step_func_done(() => { + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1); + })); + }, 0); + }, 0); +}, "navigation.traverseTo() should work in an iframe that started at a data: url"); +</script> +</body> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-cross-document.html b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-cross-document.html new file mode 100644 index 0000000000..2540639bad --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-cross-document.html @@ -0,0 +1,41 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<iframe id="i" src="/common/blank.html"></iframe> +<script> +async_test(t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + window.onload = () => t.step_timeout(() => { + assert_equals(i.contentWindow.navigation.entries().length, 1); + let next_step = "goto"; + let key = i.contentWindow.navigation.currentEntry.key; + i.contentWindow.navigation.navigate("?1"); + i.onload = t.step_func(() => { + if (next_step == "goto") { + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry, i.contentWindow.navigation.entries()[1]); + i.contentWindow.navigation.traverseTo(key); + next_step = "forward"; + } else if (next_step == "forward") { + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(key, i.contentWindow.navigation.currentEntry.key); + assert_equals(i.contentWindow.navigation.currentEntry, i.contentWindow.navigation.entries()[0]); + assert_true(i.contentWindow.navigation.canGoForward); + i.contentWindow.navigation.forward(); + next_step = "back"; + } else if (next_step == "back") { + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry, i.contentWindow.navigation.entries()[1]); + assert_true(i.contentWindow.navigation.canGoBack); + i.contentWindow.navigation.back(); + next_step = "finish"; + } else if (next_step == "finish") { + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry, i.contentWindow.navigation.entries()[0]); + t.done(); + } + }); + }, 0); +}, "cross-document traverseTo(), back(), and forward()"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-detach-between-navigate-and-navigatesuccess.html b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-detach-between-navigate-and-navigatesuccess.html new file mode 100644 index 0000000000..43bde9a103 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-detach-between-navigate-and-navigatesuccess.html @@ -0,0 +1,54 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="return-value/resources/helpers.js"></script> + +<iframe id="i" src="/common/blank.html"></iframe> + +<script> +async_test(t => { + window.onload = t.step_func(() => { + const iWindow = i.contentWindow; + const iDOMException = iWindow.DOMException; + const iNavigationHistoryEntry = iWindow.NavigationHistoryEntry; + + i.contentWindow.navigation.navigate("#1").finished.then(t.step_func(() => { + assert_equals(i.contentWindow.navigation.entries().length, 2); + let key = i.contentWindow.navigation.entries()[0].key; + + let onnavigateerror_called = false; + let result; + + i.contentWindow.navigation.onnavigate = t.step_func(e => { + // 1. The intercept handler runs. + // 2. "t.step_timeout(handlerRunResolve, 0)" executes handlerRunResolve in a macro task. + // 3. In the next microtask, the iframe is removed. + // 4. "t.step_timeout(resolve, 5)" executes and the intercept handler promise resolves. + let handlerRunResolve; + new Promise(r => handlerRunResolve = r).then(() => i.remove()); + e.intercept({ handler() { + t.step_timeout(handlerRunResolve, 0); + return new Promise(resolve => t.step_timeout(resolve, 5)); + }}); + }); + + i.contentWindow.navigation.onnavigatesuccess = t.unreached_func("navigatesuccess must not fire"); + + i.contentWindow.navigation.onnavigateerror = t.step_func(e => { + assert_false(onnavigateerror_called); + onnavigateerror_called = true; + assert_equals(e.filename, location.href); + assert_greater_than(e.lineno, 0); + assert_greater_than(e.colno, 0); + + assertCommittedFulfillsFinishedRejectsExactly(t, result, iWindow.navigation.currentEntry, e.error, iWindow, iDOMException, iNavigationHistoryEntry).then( + () => t.done(), + t.step_func(err => { throw err; }) + ); + }); + + result = i.contentWindow.navigation.traverseTo(key); + })); + }); +}, "Detach a window between when a traverseTo() fires navigate and navigatesuccess"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-multiple-steps.html b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-multiple-steps.html new file mode 100644 index 0000000000..059eb214a8 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-multiple-steps.html @@ -0,0 +1,25 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + assert_equals(navigation.entries().length, 1); + let key0 = navigation.currentEntry.key; + await navigation.navigate("#1").committed; + await navigation.navigate("#2").committed; + let key2 = navigation.currentEntry.key; + assert_equals(navigation.entries().length, 3); + + await navigation.traverseTo(key0).committed; + assert_equals(navigation.entries().length, 3); + assert_equals(navigation.currentEntry, navigation.entries()[0]); + assert_equals(key0, navigation.currentEntry.key); + await navigation.traverseTo(key2).committed; + assert_equals(navigation.entries().length, 3); + assert_equals(navigation.currentEntry, navigation.entries()[2]); + assert_equals(key2, navigation.currentEntry.key); +}, "goto() can precisely traverse multiple steps in the joint session history"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-navigates-multiple-iframes.html b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-navigates-multiple-iframes.html new file mode 100644 index 0000000000..3d6adb5341 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-navigates-multiple-iframes.html @@ -0,0 +1,45 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<iframe id="i1" src="/common/blank.html"></iframe> +<iframe id="i2" src="resources/slow-no-store.py"></iframe> +<script> +promise_test(async t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0)); + + i1.src = "/common/blank.html?navigated"; + await new Promise(resolve => i1.onload = resolve); + i2.src = "/common/blank.html?navigated"; + await new Promise(resolve => i2.onload = resolve); + assert_equals(navigation.entries().length, 1); + assert_equals(i1.contentWindow.navigation.entries().length, 2); + assert_equals(i2.contentWindow.navigation.entries().length, 2); + assert_equals(i1.contentWindow.navigation.currentEntry.index, 1); + assert_equals(i2.contentWindow.navigation.currentEntry.index, 1); + + function collectKeysAndIds(win) { + return win.navigation.entries().map(e => [e.key, e.id]).flat(); + } + let i1_keys_and_ids_before_back = collectKeysAndIds(i1.contentWindow); + let i2_keys_and_ids_before_back = collectKeysAndIds(i2.contentWindow); + + // Go back to a point that requires both frames to navigate. Because i2 is + // going back to a slow, un-cached document, i1 will likely complete before + // the server sends the response for i2. This combination of a slow and fast + // traversal is less common than the case where multiple iframes navigate at + // similar speeds, and caused a bug in chromium. + i1.contentWindow.navigation.traverseTo(i1.contentWindow.navigation.entries()[0].key); + await Promise.all( + [ new Promise(resolve => i1.onload = resolve), + new Promise(resolve => i2.onload = resolve) ]); + assert_equals(i1.contentWindow.navigation.currentEntry.index, 0); + assert_equals(i2.contentWindow.navigation.currentEntry.index, 0); + + assert_array_equals(i1_keys_and_ids_before_back, collectKeysAndIds(i1.contentWindow)); + assert_array_equals(i2_keys_and_ids_before_back, collectKeysAndIds(i2.contentWindow)); +}, "entries() should be correct after a traversal that navigates multiple browsing contexts"); +</script> +</body> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-same-document.html b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-same-document.html new file mode 100644 index 0000000000..1a32bce99c --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-same-document.html @@ -0,0 +1,43 @@ +<!doctype html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +async_test(t => { + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + window.onload = () => t.step_timeout(() => { + assert_equals(navigation.entries().length, 1); + + let onnavigate_count = 0; + navigation.onnavigate = () => onnavigate_count++; + + let key = navigation.currentEntry.key; + navigation.navigate("#").committed + .then(t.step_func(() => { + assert_equals(navigation.entries().length, 2); + assert_equals(navigation.currentEntry, navigation.entries()[1]); + assert_not_equals(key, navigation.currentEntry.key); + return navigation.traverseTo(key).committed; + })) + .then(t.step_func(() => { + assert_equals(navigation.entries().length, 2); + assert_equals(navigation.currentEntry, navigation.entries()[0]); + assert_equals(key, navigation.currentEntry.key); + assert_true(navigation.canGoForward); + return navigation.forward().committed; + })) + .then(t.step_func(() => { + assert_equals(navigation.entries().length, 2); + assert_equals(navigation.currentEntry, navigation.entries()[1]); + assert_true(navigation.canGoBack); + return navigation.back().committed; + })) + .then(t.step_func_done(() => { + assert_equals(navigation.entries().length, 2); + assert_equals(navigation.currentEntry, navigation.entries()[0]); + assert_equals(key, navigation.currentEntry.key); + assert_equals(onnavigate_count, 4); + })); + }, 0); +}, "same-document navigate.traverseTo(), back(), and forward()"); +</script> diff --git a/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-with-cross-origin-in-history.html b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-with-cross-origin-in-history.html new file mode 100644 index 0000000000..a6b7745584 --- /dev/null +++ b/testing/web-platform/tests/navigation-api/navigation-methods/traverseTo-with-cross-origin-in-history.html @@ -0,0 +1,37 @@ +<!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 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); + + let start_length = history.length; + + // Wait for after the load event so that the navigation doesn't get converted + // into a replace navigation. + window.onload = () => t.step_timeout(() => { + i.contentWindow.location = "/common/blank.html"; + i.onload = () => t.step_timeout(() => { + assert_equals(history.length, start_length + 1); + assert_equals(i.contentWindow.navigation.entries().length, 1); + i.contentWindow.navigation.navigate("#fragment"); + assert_equals(i.contentWindow.navigation.entries().length, 2); + i.contentWindow.navigation.back().committed.then(t.step_func(() => { + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry.index, 0); + return i.contentWindow.navigation.forward().committed; + })).then(t.step_func_done(() => { + assert_equals(i.contentWindow.navigation.entries().length, 2); + assert_equals(i.contentWindow.navigation.currentEntry.index, 1); + })); + }, 0); + }, 0); +}, "navigation.traverseTo() should work in an iframe that has navigated across origins"); +</script> +</body> |