summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/navigation-api/navigate-event
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/navigation-api/navigate-event')
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-origin-traversal-does-not-fire-navigate.html25
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-crossdocument-crossorigin-sameorigindomain.sub.html41
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-crossdocument-crossorigin.html30
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-crossdocument-sameorigin.html29
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-samedocument-crossorigin-sameorigindomain.sub.html41
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-samedocument-crossorigin.html39
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-samedocument-sameorigin.html27
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-crossdocument-crossorigin-sameorigindomain.sub.html34
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-crossdocument-crossorigin.html23
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-crossdocument-sameorigin.html26
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-samedocument-crossorigin-sameorigindomain.sub.html34
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-samedocument-crossorigin.html32
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-samedocument-sameorigin.html26
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-crossdocument-crossorigin-sameorigindomain.sub.html35
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-crossdocument-crossorigin.html24
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-crossdocument-sameorigin.html26
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-samedocument-crossorigin-sameorigindomain.sub.html35
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-samedocument-crossorigin.html33
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-samedocument-sameorigin.html26
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/resources/cross-origin-iframe-helper.html28
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/resources/document-domain-setter.sub.html3
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-crossdocument-crossorigin-sameorigindomain.sub.html41
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-crossdocument-crossorigin.html30
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-crossdocument-sameorigin.html27
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-samedocument-crossorigin-sameorigindomain.sub.html41
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-samedocument-crossorigin.html39
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-samedocument-sameorigin.html27
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/defaultPrevented-navigation-preempted.html19
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/defaultPrevented-window-stop-after-dispatch.html16
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/event-constructor.html96
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-after-dispatch.html16
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-and-navigate.html27
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-canceled-event.html17
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-cross-document-same-origin.html21
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-cross-origin.html18
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-detach-multiple.html18
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-detach.html17
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-handler-null-or-undefined.html21
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-handler-returns-non-promise.html18
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-handler-throws.html26
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-history-pushState.html23
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-history-replaceState.html23
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-multiple-times-reject.html38
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-multiple-times.html41
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-navigation-back.html19
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-on-synthetic-event.html17
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-popstate.html26
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-reject.html29
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-resolve.html20
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/intercept-same-document-history-back.html40
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-cross-origin.html24
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-download-userInitiated.html29
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-download.html34
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-fragment.html25
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-same-origin-cross-document.html25
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-userInitiated.html29
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-with-target.html31
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-after-detach.html33
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-dynamic-index.html34
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-getState-back-forward.html26
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-getState-navigate.html23
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-getState-reload.html25
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-form-get.html27
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-form-reload.html28
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-form-traverse.html44
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-form-userInitiated.html30
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-form-with-target.html29
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-form.html25
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-after-fragment.html31
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-after-pushState.html31
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-cross-document.html32
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-noop.html32
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-history-go-0.html27
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-history-pushState.html29
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-history-replaceState.html29
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-iframe-location.html30
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-location.html23
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-meta-refresh.html27
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-back-cross-document.html35
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-back-same-document-in-iframe.html33
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-back-same-document.html32
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-navigate.html22
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-to-javascript.html18
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-to-srcdoc.html34
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-window-open-self.html23
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigate-window-open.html30
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigatesuccess-cross-document.html14
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigatesuccess-same-document.html10
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigation-back-cross-document-preventDefault.html33
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigation-back-same-document-preventDefault.html26
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-in-iframe-same-document-preventDefault.html43
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-navigates-top-and-same-doc-child-and-cross-doc-child.html49
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-same-document-preventDefault-multiple-windows.html23
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-top-cancels-cross-document-child.html26
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/replaceState-in-unload-then-remove-iframe.html16
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/replaceState-inside-back-handler.html15
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/resources/meta-refresh.html4
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/resources/navigatesuccess-cross-document-helper.html6
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/resources/opener-postMessage-onload.html6
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/same-url-replace-cross-document.html23
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/same-url-replace-same-document.html23
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/signal-abort-detach-in-onnavigate.html21
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/signal-abort-intercept.html18
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/signal-abort-preventDefault.html19
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/signal-abort-window-stop-after-intercept.html40
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/signal-abort-window-stop-in-onnavigate.html24
-rw-r--r--testing/web-platform/tests/navigation-api/navigate-event/signal-abort-window-stop.html23
107 files changed, 2929 insertions, 0 deletions
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-origin-traversal-does-not-fire-navigate.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-origin-traversal-does-not-fire-navigate.html
new file mode 100644
index 0000000000..36491be22c
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-origin-traversal-does-not-fire-navigate.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../navigation-methods/return-value/resources/helpers.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script>
+promise_test(async t => {
+ // Open a cross-origin window.
+ let w = window.open(get_host_info().HTTP_REMOTE_ORIGIN + "/navigation-api/navigate-event/resources/opener-postMessage-onload.html");
+ await new Promise(resolve => window.onmessage = resolve);
+
+ // Navigate the opened window to this origin.
+ w.location = get_host_info().ORIGIN + "/navigation-api/navigate-event/resources/opener-postMessage-onload.html";
+ await new Promise(resolve => window.onmessage = resolve);
+ assert_equals(w.navigation.entries().length, 1);
+ assert_equals(w.navigation.currentEntry.index, 0);
+
+ // Go back. This should not fire a navigate event, because the traversal is
+ // cross-origin. Note that history.back() must be used instead of
+ // navigation.back() because navigation.back() can never go cross-origin.
+ w.navigation.onnavigate = t.unreached_func("navigate should not fire");
+ w.history.back();
+ await new Promise(resolve => window.onmessage = resolve);
+}, "A cross-origin traversal does not fire the navigate event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-crossdocument-crossorigin-sameorigindomain.sub.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-crossdocument-crossorigin-sameorigindomain.sub.html
new file mode 100644
index 0000000000..004e1a75e3
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-crossdocument-crossorigin-sameorigindomain.sub.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<script>
+document.domain = "{{host}}";
+async_test(t => {
+ const url = new URL("resources/document-domain-setter.sub.html", location.href);
+ url.hostname = "{{domains[www1]}}";
+ const iframe = document.createElement("iframe");
+ iframe.name = "windowname";
+ iframe.src = url;
+ document.body.append(iframe);
+
+ url.search = "?foo";
+ const link = document.createElement("a");
+ link.href = url;
+ link.target = iframe.name;
+ document.body.append(link);
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => {
+ iframe.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_false(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, link.href, "destination.url");
+ assert_false(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ link.click();
+ });
+}, "clicking on an <a> element that navigates cross-document targeting a same-origin-domain (but cross-origin) window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-crossdocument-crossorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-crossdocument-crossorigin.html
new file mode 100644
index 0000000000..2f40238912
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-crossdocument-crossorigin.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+
+<script>
+async_test(t => {
+ const url = new URL("resources/cross-origin-iframe-helper.html", location.href);
+ url.hostname = get_host_info().REMOTE_HOST;
+ const iframe = document.createElement("iframe");
+ iframe.src = url;
+ iframe.name = "windowname";
+ document.body.append(iframe);
+
+ url.search = "?postMessage-top-when-done";
+ const link = document.createElement("a");
+ link.href = url;
+ link.target = iframe.name;
+ document.body.append(link);
+
+ window.onmessage = t.step_func_done(e => {
+ // If we hit onnavigate in the target window, we'll get a different message, and fail.
+ assert_equals(e.data, "DONE");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => link.click());
+}, "clicking on an <a> element that navigates cross-document targeting a cross-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-crossdocument-sameorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-crossdocument-sameorigin.html
new file mode 100644
index 0000000000..683875d006
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-crossdocument-sameorigin.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<a id="link" href="/common/blank.html?foo" target="windowname">Click me</a>
+<iframe id="i" name="windowname" src="/common/blank.html"></iframe>
+
+<script>
+async_test(t => {
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => {
+ i.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_false(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, link.href, "destination.url");
+ assert_false(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ link.click();
+ });
+}, "clicking on an <a> element that navigates cross-document targeting a same-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-samedocument-crossorigin-sameorigindomain.sub.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-samedocument-crossorigin-sameorigindomain.sub.html
new file mode 100644
index 0000000000..60d1653a3a
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-samedocument-crossorigin-sameorigindomain.sub.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<script>
+document.domain = "{{host}}";
+async_test(t => {
+ const url = new URL("resources/document-domain-setter.sub.html", location.href);
+ url.hostname = "{{domains[www1]}}";
+ const iframe = document.createElement("iframe");
+ iframe.name = "windowname";
+ iframe.src = url;
+ document.body.append(iframe);
+
+ url.hash = "#foo";
+ const link = document.createElement("a");
+ link.href = url;
+ link.target = iframe.name;
+ document.body.append(link);
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => {
+ iframe.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_true(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, link.href, "destination.url");
+ assert_true(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ link.click();
+ });
+}, "clicking on an <a> element that navigates same-document targeting a same-origin-domain (but cross-origin) window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-samedocument-crossorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-samedocument-crossorigin.html
new file mode 100644
index 0000000000..ee01e7f1f2
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-samedocument-crossorigin.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+
+<script>
+async_test(t => {
+ const url = new URL("resources/cross-origin-iframe-helper.html", location.href);
+ url.hostname = get_host_info().REMOTE_HOST;
+ const iframe = document.createElement("iframe")
+ iframe.src = url;
+ iframe.name = "windowname";
+ document.body.append(iframe);
+
+ url.hash = "#foo";
+ const link = document.createElement("a");
+ link.href = url;
+ link.target = iframe.name;
+ document.body.append(link);
+
+ window.onmessage = t.step_func_done(e => {
+ assert_equals(e.data.navigationType, "push", "navigationType");
+ assert_true(e.data.cancelable, "cancelable");
+ assert_true(e.data.canIntercept, "canIntercept");
+ assert_false(e.data.userInitiated, "userInitiated");
+ assert_true(e.data.hashChange, "hashChange");
+ assert_equals(e.data.formData, null, "formData");
+ assert_equals(e.data.destination.url, link.href, "destination.url");
+ assert_true(e.data.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.data.destination.key, "", "destination.key");
+ assert_equals(e.data.destination.id, "", "destination.id");
+ assert_equals(e.data.destination.index, -1, "destination.index");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => link.click());
+}, "clicking on an <a> element that navigates same-document targeting a cross-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-samedocument-sameorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-samedocument-sameorigin.html
new file mode 100644
index 0000000000..eaa7b52345
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/click-samedocument-sameorigin.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<a id="link" href="/common/blank.html#foo" target="windowname">Click me</a>
+<iframe id="i" name="windowname" src="/common/blank.html"></iframe>
+
+<script>
+async_test(t => {
+ i.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_true(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, link.href, "destination.url");
+ assert_true(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => link.click());
+}, "clicking on an <a> element that navigates same-document targeting a same-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-crossdocument-crossorigin-sameorigindomain.sub.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-crossdocument-crossorigin-sameorigindomain.sub.html
new file mode 100644
index 0000000000..cee7d95b9e
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-crossdocument-crossorigin-sameorigindomain.sub.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<script>
+document.domain = "{{host}}";
+async_test(t => {
+ const url = new URL("resources/document-domain-setter.sub.html", location.href);
+ url.hostname = "{{domains[www1]}}";
+ const iframe = document.createElement("iframe");
+ iframe.src = url;
+ document.body.append(iframe);
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => {
+ iframe.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_false(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, iframe.src + "?foo", "destination.url");
+ assert_false(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ iframe.contentWindow.location.href = url + "?foo";
+ });
+}, "using location.href to navigate cross-document targeting a same-origin-domain (but cross-origin) window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-crossdocument-crossorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-crossdocument-crossorigin.html
new file mode 100644
index 0000000000..79df86fdc9
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-crossdocument-crossorigin.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+
+<script>
+async_test(t => {
+ const iframeURL = new URL("resources/cross-origin-iframe-helper.html", location.href);
+ iframeURL.hostname = get_host_info().REMOTE_HOST;
+ const iframe = document.createElement("iframe");
+ iframe.src = iframeURL;
+ document.body.append(iframe);
+
+ window.onmessage = t.step_func_done(e => {
+ // If we hit onnavigate in the target window, we'll get a different message, and fail.
+ assert_equals(e.data, "DONE");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => iframe.contentWindow.location.href = iframeURL + "?postMessage-top-when-done");
+}, "using location.href to navigate cross-document targeting a cross-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-crossdocument-sameorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-crossdocument-sameorigin.html
new file mode 100644
index 0000000000..d0dad46b72
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-crossdocument-sameorigin.html
@@ -0,0 +1,26 @@
+<!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 => {
+ i.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_false(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, i.src + "?foo", "destination.url");
+ assert_false(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => i.contentWindow.location.href = "/common/blank.html?foo");
+}, "using location.href to navigate cross-document targeting a same-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-samedocument-crossorigin-sameorigindomain.sub.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-samedocument-crossorigin-sameorigindomain.sub.html
new file mode 100644
index 0000000000..156d1cb3cf
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-samedocument-crossorigin-sameorigindomain.sub.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<script>
+document.domain = "{{host}}";
+async_test(t => {
+ const url = new URL("resources/document-domain-setter.sub.html", location.href);
+ url.hostname = "{{domains[www1]}}";
+ const iframe = document.createElement("iframe");
+ iframe.src = url;
+ document.body.append(iframe);
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => {
+ iframe.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_true(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, iframe.src + "#foo", "destination.url");
+ assert_true(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ iframe.contentWindow.location.href = url + "#foo";
+ });
+}, "using location.href to navigate same-document targeting a same-origin-domain (but cross-origin) window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-samedocument-crossorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-samedocument-crossorigin.html
new file mode 100644
index 0000000000..6d73262dea
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-samedocument-crossorigin.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+
+<script>
+async_test(t => {
+ const iframeURL = new URL("resources/cross-origin-iframe-helper.html", location.href);
+ iframeURL.hostname = get_host_info().REMOTE_HOST;
+ const iframe = document.createElement("iframe")
+ iframe.src = iframeURL;
+ document.body.append(iframe);
+
+ window.onmessage = t.step_func_done(e => {
+ assert_equals(e.data.navigationType, "push", "navigationType");
+ assert_true(e.data.cancelable, "cancelable");
+ assert_true(e.data.canIntercept, "canIntercept");
+ assert_false(e.data.userInitiated, "userInitiated");
+ assert_true(e.data.hashChange, "hashChange");
+ assert_equals(e.data.formData, null, "formData");
+ assert_equals(e.data.destination.url, iframe.src + "#foo", "destination.url");
+ assert_true(e.data.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.data.destination.key, "", "destination.key");
+ assert_equals(e.data.destination.id, "", "destination.id");
+ assert_equals(e.data.destination.index, -1, "destination.index");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => iframe.contentWindow.location.href = iframeURL + "#foo");
+}, "using location.href to navigate same-document targeting a cross-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-samedocument-sameorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-samedocument-sameorigin.html
new file mode 100644
index 0000000000..6516a60239
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/location-samedocument-sameorigin.html
@@ -0,0 +1,26 @@
+<!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 => {
+ i.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_true(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, i.src + "#foo", "destination.url");
+ assert_true(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => i.contentWindow.location.href = "/common/blank.html#foo");
+}, "using location.href to navigate same-document targeting a same-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-crossdocument-crossorigin-sameorigindomain.sub.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-crossdocument-crossorigin-sameorigindomain.sub.html
new file mode 100644
index 0000000000..1c411d9866
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-crossdocument-crossorigin-sameorigindomain.sub.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<script>
+document.domain = "{{host}}";
+async_test(t => {
+ const url = new URL("resources/document-domain-setter.sub.html", location.href);
+ url.hostname = "{{domains[www1]}}";
+ const iframe = document.createElement("iframe");
+ iframe.name = "windowname";
+ iframe.src = url;
+ document.body.append(iframe);
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => {
+ iframe.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_false(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, url + "?foo", "destination.url");
+ assert_false(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ window.open(url + "?foo", iframe.name);
+ });
+}, "using window.open() to navigate cross-document targeting a same-origin-domain (but cross-origin) window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-crossdocument-crossorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-crossdocument-crossorigin.html
new file mode 100644
index 0000000000..73ede89cbf
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-crossdocument-crossorigin.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+
+<script>
+async_test(t => {
+ const iframeURL = new URL("resources/cross-origin-iframe-helper.html", location.href);
+ iframeURL.hostname = get_host_info().REMOTE_HOST;
+ const iframe = document.createElement("iframe");
+ iframe.src = iframeURL;
+ iframe.name = "windowname";
+ document.body.append(iframe);
+
+ window.onmessage = t.step_func_done(e => {
+ // If we hit onnavigate in the target window, we'll get a different message, and fail.
+ assert_equals(e.data, "DONE");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => window.open(iframeURL + "?postMessage-top-when-done", "windowname"));
+}, "using window.open() to navigate cross-document targeting a cross-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-crossdocument-sameorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-crossdocument-sameorigin.html
new file mode 100644
index 0000000000..c91689341e
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-crossdocument-sameorigin.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id="i" name="windowname" src="/common/blank.html"></iframe>
+
+<script>
+async_test(t => {
+ i.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_false(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, i.src + "?foo", "destination.url");
+ assert_false(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => window.open("/common/blank.html?foo", "windowname"));
+}, "using window.open() to navigate cross-document targeting a same-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-samedocument-crossorigin-sameorigindomain.sub.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-samedocument-crossorigin-sameorigindomain.sub.html
new file mode 100644
index 0000000000..74a6cae246
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-samedocument-crossorigin-sameorigindomain.sub.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<script>
+document.domain = "{{host}}";
+async_test(t => {
+ const url = new URL("resources/document-domain-setter.sub.html", location.href);
+ url.hostname = "{{domains[www1]}}";
+ const iframe = document.createElement("iframe");
+ iframe.name = "windowname";
+ iframe.src = url;
+ document.body.append(iframe);
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => {
+ iframe.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_true(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, url + "#foo", "destination.url");
+ assert_true(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ window.open(url + "#foo", iframe.name);
+ });
+}, "using window.open() to navigate same-document targeting a same-origin-domain (but cross-origin) window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-samedocument-crossorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-samedocument-crossorigin.html
new file mode 100644
index 0000000000..9b31744ba3
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-samedocument-crossorigin.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+
+<script>
+async_test(t => {
+ const iframeURL = new URL("resources/cross-origin-iframe-helper.html", location.href);
+ iframeURL.hostname = get_host_info().REMOTE_HOST;
+ const iframe = document.createElement("iframe")
+ iframe.src = iframeURL;
+ iframe.name = "windowname";
+ document.body.append(iframe);
+
+ window.onmessage = t.step_func_done(e => {
+ assert_equals(e.data.navigationType, "push", "navigationType");
+ assert_true(e.data.cancelable, "cancelable");
+ assert_true(e.data.canIntercept, "canIntercept");
+ assert_false(e.data.userInitiated, "userInitiated");
+ assert_true(e.data.hashChange, "hashChange");
+ assert_equals(e.data.formData, null, "formData");
+ assert_equals(e.data.destination.url, iframe.src + "#foo", "destination.url");
+ assert_true(e.data.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.data.destination.key, "", "destination.key");
+ assert_equals(e.data.destination.id, "", "destination.id");
+ assert_equals(e.data.destination.index, -1, "destination.index");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => window.open(iframeURL + "#foo", "windowname"));
+}, "using window.open() to navigate same-document targeting a cross-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-samedocument-sameorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-samedocument-sameorigin.html
new file mode 100644
index 0000000000..c357072e09
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/open-samedocument-sameorigin.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id="i" name="windowname" src="/common/blank.html"></iframe>
+
+<script>
+async_test(t => {
+ i.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_true(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, i.src + "#foo", "destination.url");
+ assert_true(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => window.open("/common/blank.html#foo", "windowname"));
+}, "using window.open() to navigate same-document targeting a same-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/resources/cross-origin-iframe-helper.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/resources/cross-origin-iframe-helper.html
new file mode 100644
index 0000000000..3393a2ecaa
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/resources/cross-origin-iframe-helper.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<script>
+navigation.onnavigate = e => {
+ const message = {
+ navigationType: e.navigationType,
+ cancelable: e.cancelable,
+ canIntercept: e.canIntercept,
+ userInitiated: e.userInitiated,
+ hashChange: e.hashChange,
+ formData: e.formData,
+ destination: {
+ url: e.destination.url,
+ sameDocument: e.destination.sameDocument,
+ key: e.destination.key,
+ id: e.destination.id,
+ index: e.destination.index
+ }
+ };
+ e.preventDefault();
+ top.postMessage(message, "*");
+};
+
+window.onload = () => {
+ if (location.href.includes("postMessage-top-when-done")) {
+ top.postMessage("DONE", "*");
+ }
+};
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/resources/document-domain-setter.sub.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/resources/document-domain-setter.sub.html
new file mode 100644
index 0000000000..abe32ad8fb
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/resources/document-domain-setter.sub.html
@@ -0,0 +1,3 @@
+<script>
+document.domain = "{{host}}";
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-crossdocument-crossorigin-sameorigindomain.sub.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-crossdocument-crossorigin-sameorigindomain.sub.html
new file mode 100644
index 0000000000..ffdaac7ccd
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-crossdocument-crossorigin-sameorigindomain.sub.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<script>
+document.domain = "{{host}}";
+async_test(t => {
+ const url = new URL("resources/document-domain-setter.sub.html?start", location.href);
+ url.hostname = "{{domains[www1]}}";
+ const iframe = document.createElement("iframe");
+ iframe.name = "windowname";
+ iframe.src = url;
+ document.body.append(iframe);
+
+ url.search = ""; // setting to "?" actually erases it anyway
+ const form = document.createElement("form");
+ form.action = url + "?";
+ form.target = iframe.name;
+ document.body.append(form);
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => {
+ iframe.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_false(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, form.action, "destination.url");
+ assert_false(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ form.submit();
+ });
+}, "submitting a <form> element that navigates cross-document targeting a same-origin-domain (but cross-origin) window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-crossdocument-crossorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-crossdocument-crossorigin.html
new file mode 100644
index 0000000000..007f58b1fb
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-crossdocument-crossorigin.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+
+<script>
+async_test(t => {
+ const url = new URL("resources/cross-origin-iframe-helper.html", location.href);
+ url.hostname = get_host_info().REMOTE_HOST;
+ const iframe = document.createElement("iframe")
+ iframe.src = url;
+ iframe.name = "windowname";
+ document.body.append(iframe);
+
+ const form = document.createElement("form");
+ form.action = url;
+ form.target = iframe.name;
+ form.innerHTML = `<input type="hidden" name="postMessage-top-when-done" value="yes">`;
+ document.body.append(form);
+
+ window.onmessage = t.step_func_done(e => {
+ // If we hit onnavigate in the target window, we'll get a different message, and fail.
+ assert_equals(e.data, "DONE");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => form.submit());
+}, "submitting a <form> element that navigates cross-document targeting a cross-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-crossdocument-sameorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-crossdocument-sameorigin.html
new file mode 100644
index 0000000000..b54a5a440b
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-crossdocument-sameorigin.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<form id="form" action="/common/blank.html?" target="windowname"></form>
+<iframe id="i" name="windowname" src="/common/blank.html?start"></iframe>
+
+<script>
+async_test(t => {
+ i.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_false(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, form.action, "destination.url");
+ assert_false(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => form.submit());
+}, "submitting a <form> element that navigates cross-document targeting a same-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-samedocument-crossorigin-sameorigindomain.sub.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-samedocument-crossorigin-sameorigindomain.sub.html
new file mode 100644
index 0000000000..e9ab17243e
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-samedocument-crossorigin-sameorigindomain.sub.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<script>
+document.domain = "{{host}}";
+async_test(t => {
+ const url = new URL("resources/document-domain-setter.sub.html?", location.href);
+ url.hostname = "{{domains[www1]}}";
+ const iframe = document.createElement("iframe");
+ iframe.name = "windowname";
+ iframe.src = url;
+ document.body.append(iframe);
+
+ url.hash = "#foo";
+ const form = document.createElement("form");
+ form.action = url;
+ form.target = iframe.name;
+ document.body.append(form);
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => {
+ iframe.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_true(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, form.action, "destination.url");
+ assert_true(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ form.submit();
+ });
+}, "submitting a <form> element that navigates cross-document targeting a same-origin-domain (but cross-origin) window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-samedocument-crossorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-samedocument-crossorigin.html
new file mode 100644
index 0000000000..69b12f2795
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-samedocument-crossorigin.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+
+<script>
+async_test(t => {
+ const url = new URL("resources/cross-origin-iframe-helper.html?", location.href);
+ url.hostname = get_host_info().REMOTE_HOST;
+ const iframe = document.createElement("iframe")
+ iframe.src = url;
+ iframe.name = "windowname";
+ document.body.append(iframe);
+
+ url.hash = "#foo";
+ const form = document.createElement("form");
+ form.action = url;
+ form.target = iframe.name;
+ document.body.append(form);
+
+ window.onmessage = t.step_func_done(e => {
+ assert_equals(e.data.navigationType, "push", "navigationType");
+ assert_true(e.data.cancelable, "cancelable");
+ assert_true(e.data.canIntercept, "canIntercept");
+ assert_false(e.data.userInitiated, "userInitiated");
+ assert_true(e.data.hashChange, "hashChange");
+ assert_equals(e.data.formData, null, "formData");
+ assert_equals(e.data.destination.url, form.action, "destination.url");
+ assert_true(e.data.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.data.destination.key, "", "destination.key");
+ assert_equals(e.data.destination.id, "", "destination.id");
+ assert_equals(e.data.destination.index, -1, "destination.index");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => form.submit());
+}, "submitting a <form> element that navigates same-document targeting a cross-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-samedocument-sameorigin.html b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-samedocument-sameorigin.html
new file mode 100644
index 0000000000..8a0e1f1fb6
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/cross-window/submit-samedocument-sameorigin.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<form id="form" action="/common/blank.html?#foo" target="windowname"></form>
+<iframe id="i" name="windowname" src="/common/blank.html?"></iframe>
+
+<script>
+async_test(t => {
+ i.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push", "navigationType");
+ assert_true(e.cancelable, "cancelable");
+ assert_true(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_true(e.hashChange, "hashChange");
+ assert_equals(e.formData, null, "formData");
+ assert_equals(e.destination.url, form.action, "destination.url");
+ assert_true(e.destination.sameDocument, "destination.sameDocument");
+ assert_equals(e.destination.key, "", "destination.key");
+ assert_equals(e.destination.id, "", "destination.id");
+ assert_equals(e.destination.index, -1, "destination.index");
+ });
+
+ navigation.onnavigate = t.unreached_func("onnavigate must not fire in the source window");
+ window.onload = t.step_func(() => form.submit());
+}, "submitting a <form> element that navigates same-document targeting a same-origin window");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/defaultPrevented-navigation-preempted.html b/testing/web-platform/tests/navigation-api/navigate-event/defaultPrevented-navigation-preempted.html
new file mode 100644
index 0000000000..1df4f56c5e
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/defaultPrevented-navigation-preempted.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+ let navigateEvent;
+ let navigateError_called = false;
+ navigation.onnavigate = e => navigateEvent = e;
+ navigation.onnavigateerror = () => navigateError_called = true;
+
+ navigation.navigate("#1");
+ assert_false(navigateEvent.defaultPrevented);
+ assert_false(navigateError_called);
+
+ navigation.navigate("#2");
+ assert_false(navigateEvent.defaultPrevented);
+ assert_true(navigateError_called);
+}, "navigateEvent.defaultPrevented isn't affected when the navigation is preempted after dispatch");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/defaultPrevented-window-stop-after-dispatch.html b/testing/web-platform/tests/navigation-api/navigate-event/defaultPrevented-window-stop-after-dispatch.html
new file mode 100644
index 0000000000..da5de10ddd
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/defaultPrevented-window-stop-after-dispatch.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ window.onload = t.step_func_done(() => {
+ let navigateEvent;
+ navigation.onnavigate = e => navigateEvent = e;
+ navigation.navigate("?1");
+ assert_false(navigateEvent.defaultPrevented);
+
+ window.stop();
+ assert_false(navigateEvent.defaultPrevented);
+ });
+}, "window.stop() doesn't affect navigateEvent.defaultPrevented after dispatch");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/event-constructor.html b/testing/web-platform/tests/navigation-api/navigate-event/event-constructor.html
new file mode 100644
index 0000000000..065884240e
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/event-constructor.html
@@ -0,0 +1,96 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+ assert_throws_js(TypeError, () => {
+ new NavigateEvent("navigate");
+ });
+}, "can't bypass required members by omitting the dictionary entirely");
+
+test(() => {
+ assert_throws_js(TypeError, () => {
+ new NavigateEvent("navigate", {
+ navigationType: "push",
+ canIntercept: false,
+ userInitiated: false,
+ hashChange: false,
+ signal: (new AbortController()).signal,
+ formData: null,
+ downloadRequest: null,
+ info: null
+ });
+ });
+}, "destination is required");
+
+async_test(t => {
+ // We need to grab an NavigationDestination.
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_throws_js(TypeError, () => {
+ new NavigateEvent("navigate", {
+ navigationType: "push",
+ destination: e.destination,
+ canIntercept: false,
+ userInitiated: false,
+ hashChange: false,
+ formData: null,
+ downloadRequest: null,
+ info: null
+ });
+ });
+ });
+ history.pushState(1, null, "#1");
+}, "signal is required");
+
+async_test(t => {
+ // We need to grab an NavigationDestination.
+ navigation.onnavigate = t.step_func_done(e => {
+ const info = { some: "object with identity" };
+ const formData = new FormData();
+ const signal = (new AbortController()).signal;
+ const downloadRequest = "abc";
+
+ const event = new NavigateEvent("navigate", {
+ navigationType: "replace",
+ destination: e.destination,
+ canIntercept: true,
+ userInitiated: true,
+ hashChange: true,
+ signal,
+ formData,
+ downloadRequest,
+ info
+ });
+
+ assert_equals(event.navigationType, "replace");
+ assert_equals(event.destination, e.destination);
+ assert_equals(event.canIntercept, true);
+ assert_equals(event.userInitiated, true);
+ assert_equals(event.hashChange, true);
+ assert_equals(event.signal, signal);
+ assert_equals(event.formData, formData);
+ assert_equals(event.downloadRequest, downloadRequest);
+ assert_equals(event.info, info);
+ });
+ history.pushState(2, null, "#2");
+}, "all properties are reflected back");
+
+async_test(t => {
+ // We need to grab an NavigationDestination.
+ navigation.onnavigate = t.step_func_done(e => {
+ const event = new NavigateEvent("navigate", {
+ destination: e.destination,
+ signal: (new AbortController()).signal
+ });
+
+ assert_equals(event.navigationType, "push");
+ assert_equals(event.canIntercept, false);
+ assert_equals(event.userInitiated, false);
+ assert_equals(event.hashChange, false);
+ assert_equals(event.formData, null);
+ assert_equals(event.downloadRequest, null);
+ assert_equals(event.info, undefined);
+ });
+ history.pushState(3, null, "#3");
+}, "defaults are as expected");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-after-dispatch.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-after-dispatch.html
new file mode 100644
index 0000000000..abb328050d
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-after-dispatch.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 => {
+ navigation.onnavigate = t.step_func(e => {
+ t.step_timeout(t.step_func_done(() => {
+ assert_equals(e.eventPhase, Event.NONE);
+ assert_throws_dom("InvalidStateError", () => e.intercept());
+ }), 0);
+ });
+
+ location.href = "#1";
+}, "event.intercept() throws if used after the dispatch phase");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-and-navigate.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-and-navigate.html
new file mode 100644
index 0000000000..dfbb67b6b6
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-and-navigate.html
@@ -0,0 +1,27 @@
+<!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(r => window.onload = () => t.step_timeout(r, 0));
+ await navigation.navigate("#1").finished;
+
+ navigation.onnavigate = e => e.intercept();
+ navigation.oncurrententrychange = e => {
+ if (e.navigationType == "traverse") {
+ assert_equals(location.hash, "");
+ assert_equals(navigation.currentEntry.index, 0);
+ assert_equals(navigation.entries().length, 2);
+ navigation.navigate("#2");
+ }
+ };
+ let back_result = navigation.back();
+ await back_result.committed;
+ assert_equals(location.hash, "#2");
+ await promise_rejects_dom(t, "AbortError", back_result.finished);
+ assert_equals(navigation.currentEntry.index, 1);
+ assert_equals(navigation.entries().length, 2);
+}, "Using intercept() then navigate() in the ensuing currententrychange should abort the finished promise (but not the committed promise)");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-canceled-event.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-canceled-event.html
new file mode 100644
index 0000000000..d4b9633c1a
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-canceled-event.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="/common/blank.html"></iframe>
+<script>
+test(t => {
+ let assertionHappened = false;
+ navigation.onnavigate = t.step_func_done(e => {
+ e.preventDefault();
+ assert_throws_dom("InvalidStateError", () => e.intercept());
+ assertionHappened = true;
+ });
+
+ location.href = "#1";
+ assert_true(assertionHappened);
+}, "event.intercept() throws if used on a canceled event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-cross-document-same-origin.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-cross-document-same-origin.html
new file mode 100644
index 0000000000..0d610cce4f
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-cross-document-same-origin.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script>
+async_test(t => {
+ let target_url = location.href + "?1";
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ e.intercept({ handler: async () => {
+ await Promise.resolve();
+ t.step_func_done(() => assert_equals(location.href, target_url));
+ }});
+ });
+
+ window.onload = t.step_func(() => location.href = target_url);
+}, "event.intercept() intercepts a same-origin cross-document navigation");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-cross-origin.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-cross-origin.html
new file mode 100644
index 0000000000..b367df546c
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-cross-origin.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script>
+async_test(t => {
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_true(e.cancelable);
+ assert_false(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_throws_dom("SecurityError", () => e.intercept());
+ e.preventDefault();
+ });
+
+ window.onload = t.step_func(() => location.href = get_host_info().HTTPS_REMOTE_ORIGIN);
+}, "event.intercept() should throw if called for a cross origin navigation");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-detach-multiple.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-detach-multiple.html
new file mode 100644
index 0000000000..5b6a623284
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-detach-multiple.html
@@ -0,0 +1,18 @@
+<!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(() => {
+ let second_handler_run = false;
+ i.contentWindow.navigation.onnavigate = e => {
+ e.intercept({ handler() { i.remove(); }});
+ e.intercept({ handler() { second_handler_run = true; }});
+ };
+
+ i.contentWindow.location.href = "#1";
+ t.step_timeout(t.step_func_done(() => assert_true(second_handler_run)), 0);
+ });
+}, "event.intercept() throws if used on an event from a detached iframe");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-detach.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-detach.html
new file mode 100644
index 0000000000..b5ce45aa29
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-detach.html
@@ -0,0 +1,17 @@
+<!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.onnavigate = t.step_func_done(e => {
+ let iframe_constructor = i.contentWindow.DOMException;
+ i.remove();
+ assert_throws_dom("InvalidStateError", iframe_constructor, () => e.intercept());
+ });
+
+ i.contentWindow.location.href = "#1";
+ });
+}, "event.intercept() throws if used on an event from a detached iframe");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-handler-null-or-undefined.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-handler-null-or-undefined.html
new file mode 100644
index 0000000000..7f5bd6b19f
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-handler-null-or-undefined.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_throws_js(TypeError, () => e.intercept({ handler: null }));
+ });
+
+ location.href = "#1";
+ assert_equals(location.hash, "#1");
+}, "event.intercept() should throw if the handler is null");
+
+async_test(t => {
+ navigation.onnavigatesuccess = t.step_func_done(e => {});
+ navigation.onnavigate = e => e.intercept({ handler: undefined });
+
+ location.href = "#2";
+ assert_equals(location.hash, "#2");
+}, "event.intercept() should not throw if the handler is explicit undefined");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-handler-returns-non-promise.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-handler-returns-non-promise.html
new file mode 100644
index 0000000000..96116e9295
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-handler-returns-non-promise.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ navigation.onnavigatesuccess = t.step_func_done(e => {
+ assert_equals(location.hash, "#1");
+ assert_equals(e.constructor, Event);
+ assert_false(e.bubbles);
+ assert_false(e.cancelable);
+ });
+ navigation.onnavigateerror = t.step_func_done(assert_unreached);
+ navigation.onnavigate = e => e.intercept({ handler() { return "123"; }});
+
+ location.href = "#1";
+ assert_equals(location.hash, "#1");
+}, "event.intercept() should resolve immediately if the handler doesn't return a promise");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-handler-throws.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-handler-throws.html
new file mode 100644
index 0000000000..769f675999
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-handler-throws.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ const err = new TypeError("a message");
+ let start_href = location.href;
+
+ navigation.onnavigatesuccess = t.step_func_done(assert_unreached);
+ navigation.onnavigateerror = t.step_func_done(e => {
+ assert_equals(location.hash, "#1");
+ assert_equals(e.constructor, ErrorEvent);
+ assert_true(e.error === err);
+ assert_equals(e.message, "Uncaught TypeError: a message");
+ assert_equals(e.filename, start_href);
+ assert_greater_than(e.colno, 0);
+ assert_greater_than(e.lineno, 0);
+ });
+ navigation.onnavigate = e => {
+ e.intercept({ handler() { throw err; }});
+ };
+
+ location.href = "#1";
+ assert_equals(location.hash, "#1");
+}, "event.intercept() should abort if the handler throws");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-history-pushState.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-history-pushState.html
new file mode 100644
index 0000000000..aad08b742e
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-history-pushState.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ let start_length = history.length;
+ navigation.onnavigate = t.step_func(e => {
+ e.intercept({ handler: async () => {
+ await new Promise(r => t.step_timeout(r, 0));
+ t.step_timeout(t.step_func_done(() => {
+ assert_equals(location.hash, "#1");
+ assert_equals(history.state, "update");
+ assert_equals(history.length, start_length + 1);
+ }, 0));
+ }});
+ });
+
+ history.pushState("update", "", "#1");
+ assert_equals(location.hash, "#1");
+ assert_equals(history.state, "update");
+ assert_equals(history.length, start_length + 1);
+}, "event.intercept() should proceed if the given promise resolves");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-history-replaceState.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-history-replaceState.html
new file mode 100644
index 0000000000..16edec8596
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-history-replaceState.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ let start_length = history.length;
+ navigation.onnavigate = t.step_func(e => {
+ e.intercept({ handler: async () => {
+ await new Promise(r => t.step_timeout(r, 0));
+ t.step_timeout(t.step_func_done(() => {
+ assert_equals(location.hash, "#1");
+ assert_equals(history.state, "update");
+ assert_equals(history.length, start_length);
+ }, 0));
+ }});
+ });
+
+ history.replaceState("update", "", "#1");
+ assert_equals(location.hash, "#1");
+ assert_equals(history.state, "update");
+ assert_equals(history.length, start_length);
+}, "event.intercept() should proceed if the given promise resolves");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-multiple-times-reject.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-multiple-times-reject.html
new file mode 100644
index 0000000000..0b0f1f0b8e
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-multiple-times-reject.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+promise_test(async t => {
+ const err = new TypeError("a message");
+ let start_href = location.href;
+
+ let onnavigateerror_called = false;
+ let caught_rejection = false;
+ navigation.onnavigatesuccess = t.step_func(assert_unreached);
+ navigation.onnavigateerror = t.step_func(e => {
+ onnavigateerror_called = true;
+ assert_equals(location.hash, "#1");
+ assert_equals(e.constructor, ErrorEvent);
+ assert_equals(e.error, err);
+ assert_equals(e.message, "Uncaught TypeError: a message");
+ assert_equals(e.filename, start_href);
+ assert_greater_than(e.colno, 0);
+ assert_greater_than(e.lineno, 0);
+ });
+ navigation.onnavigate = t.step_func(e => {
+ e.intercept();
+ e.intercept({ handler: async () => {
+ await new Promise(r => t.step_timeout(r, 1));
+ return Promise.reject(err);
+ }});
+ e.intercept({ handler: () => new Promise(resolve => t.step_timeout(resolve, 1)) });
+ });
+
+ await navigation.navigate("#1").finished.catch(t.step_func(e => {
+ caught_rejection = true;
+ assert_equals(e, err);
+ }));
+ assert_true(onnavigateerror_called);
+ assert_true(caught_rejection);
+}, "event.intercept() is called multiple times and one of the promises rejects");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-multiple-times.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-multiple-times.html
new file mode 100644
index 0000000000..6deaeeb507
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-multiple-times.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+promise_test(async t => {
+ let p1_resolved = false;
+ let p2_resolved = false;
+ let p3_resolved = false;
+ navigation.onnavigate = t.step_func(e => {
+ e.intercept({ handler: async () => {
+ await Promise.resolve();
+ assert_false(p2_resolved);
+ assert_false(p3_resolved);
+ p1_resolved = true;
+ }});
+ e.intercept({ handler: async () => {
+ await new Promise(resolve => t.step_timeout(resolve, 1));
+ assert_true(p1_resolved);
+ assert_false(p3_resolved);
+ p2_resolved = true;
+ }});
+ e.intercept({ handler: async () => {
+ await new Promise(resolve => t.step_timeout(resolve, 1));
+ assert_true(p1_resolved);
+ assert_true(p2_resolved);
+ p3_resolved = true;
+ }});
+ });
+
+ let promise = navigation.navigate("#1").finished;
+ assert_equals(location.hash, "#1");
+ assert_false(p1_resolved);
+ assert_false(p2_resolved);
+ assert_false(p3_resolved);
+
+ await promise;
+ assert_true(p1_resolved);
+ assert_true(p2_resolved);
+ assert_true(p3_resolved);
+}, "navigation.navigate() returns a finished promise that awaits all promises if event.intercept() is called multiple times");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-navigation-back.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-navigation-back.html
new file mode 100644
index 0000000000..8babf2bcdc
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-navigation-back.html
@@ -0,0 +1,19 @@
+<!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(() => {
+ navigation.navigate("#foo").committed.then(() => {
+ assert_true(navigation.canGoBack);
+ navigation.onnavigate = t.step_func(e => e.intercept());
+ navigation.back().committed.then(t.step_func_done(() => {
+ assert_equals(navigation.entries().length, 2);
+ assert_equals(navigation.currentEntry, navigation.entries()[0]);
+ }));
+ });
+ }, 0);
+}, "event.intercept() can intercept navigation.back()");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-on-synthetic-event.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-on-synthetic-event.html
new file mode 100644
index 0000000000..3a4b54de5e
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-on-synthetic-event.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ // We need to grab an NavigationDestination to construct the event.
+ navigation.onnavigate = t.step_func_done(e => {
+ const event = new NavigateEvent("navigate", {
+ destination: e.destination,
+ signal: (new AbortController()).signal
+ });
+
+ assert_throws_dom("SecurityError", () => event.intercept());
+ });
+ history.pushState(1, null, "#1");
+}, "event.intercept() throws if invoked on a synthetic event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-popstate.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-popstate.html
new file mode 100644
index 0000000000..f5f9d82be7
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-popstate.html
@@ -0,0 +1,26 @@
+<!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));
+ history.replaceState({ state: "foo"}, "", "#replace");
+
+ let onpopstate_fired = false;
+ let last_state;
+ window.onpopstate = e => {
+ onpopstate_fired = true;
+ last_state = e.state;
+ }
+ navigation.onnavigate = t.step_func(e => e.intercept());
+
+ await navigation.navigate("#").finished;
+ assert_true(navigation.canGoBack);
+
+ await navigation.back().finished;
+ assert_true(onpopstate_fired);
+ assert_not_equals(last_state, null);
+}, "event.intercept() should provide popstate with a valid state object");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-reject.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-reject.html
new file mode 100644
index 0000000000..4c5ec0163d
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-reject.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ const err = new TypeError("a message");
+ let start_href = location.href;
+
+ navigation.onnavigatesuccess = t.step_func_done(assert_unreached);
+ navigation.onnavigateerror = t.step_func_done(e => {
+ assert_equals(location.hash, "#1");
+ assert_equals(e.constructor, ErrorEvent);
+ assert_true(e.error === err);
+ assert_equals(e.message, "Uncaught TypeError: a message");
+ assert_equals(e.filename, start_href);
+ assert_greater_than(e.colno, 0);
+ assert_greater_than(e.lineno, 0);
+ });
+ navigation.onnavigate = e => {
+ e.intercept({ handler: async () => {
+ await new Promise(r => t.step_timeout(r, 0));
+ return Promise.reject(err);
+ }});
+ };
+
+ location.href = "#1";
+ assert_equals(location.hash, "#1");
+}, "event.intercept() should abort if the given promise rejects");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-resolve.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-resolve.html
new file mode 100644
index 0000000000..b60d89b5df
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-resolve.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ navigation.onnavigatesuccess = t.step_func_done(e => {
+ assert_equals(location.hash, "#1");
+ assert_equals(e.constructor, Event);
+ assert_false(e.bubbles);
+ assert_false(e.cancelable);
+ });
+ navigation.onnavigateerror = t.step_func_done(assert_unreached);
+ navigation.onnavigate = e => {
+ e.intercept({ handler: () => new Promise(resolve => t.step_timeout(resolve, 0)) });
+ };
+
+ location.href = "#1";
+ assert_equals(location.hash, "#1");
+}, "event.intercept() should proceed if the given promise resolves");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/intercept-same-document-history-back.html b/testing/web-platform/tests/navigation-api/navigate-event/intercept-same-document-history-back.html
new file mode 100644
index 0000000000..6faabe1964
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/intercept-same-document-history-back.html
@@ -0,0 +1,40 @@
+<!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(() => {
+ let onnavigate_calls = 0;
+ let onnavigatesuccess_calls = 0;
+ navigation.onnavigate = e => {
+ onnavigate_calls++;
+ e.intercept();
+ }
+ navigation.onnavigatesuccess = t.step_func(e => {
+ onnavigatesuccess_calls++;
+ if (onnavigatesuccess_calls == 3) {
+ assert_equals(navigation.entries().length, 3);
+ assert_equals(navigation.currentEntry.index, 1);
+ assert_equals(onnavigate_calls, 3);
+ history.back();
+ } else if (onnavigatesuccess_calls == 4) {
+ assert_equals(navigation.entries().length, 3);
+ assert_equals(navigation.currentEntry.index, 0);
+ assert_equals(onnavigate_calls, 4);
+ t.done();
+ }
+ });
+
+ navigation.navigate("?foo").finished
+ .then(t.step_func(() => navigation.navigate("?bar").finished))
+ .then(t.step_func(() => {
+ assert_equals(navigation.entries().length, 3);
+ assert_equals(navigation.currentEntry.index, 2);
+ assert_equals(onnavigate_calls, 2);
+ history.back();
+ }));
+ }, 0);
+}, "event.intercept() can intercept same-document history.back()");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-cross-origin.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-cross-origin.html
new file mode 100644
index 0000000000..ee09924850
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-cross-origin.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<a id="a" href="https://does-not-exist/foo.html"></a>
+<script>
+async_test(t => {
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push");
+ assert_true(e.cancelable);
+ assert_false(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.formData, null);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(e.destination.url, "https://does-not-exist/foo.html");
+ assert_false(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ e.preventDefault();
+ });
+ a.click();
+}, "<a> cross-origin navigate event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-download-userInitiated.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-download-userInitiated.html
new file mode 100644
index 0000000000..b9506984da
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-download-userInitiated.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<a id="a" href="?1">Click me</a>
+<script>
+async_test(t => {
+ a.download = "";
+ navigation.onnavigate = t.step_func(e => {
+ assert_equals(e.navigationType, "push");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_true(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.downloadRequest, "");
+ assert_equals(e.formData, null);
+ assert_equals(new URL(e.destination.url).search, "?1");
+ assert_false(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ e.preventDefault();
+ t.step_timeout(t.step_func_done(() => assert_equals(location.hash, "")), 0);
+ });
+ test_driver.click(a);
+}, "<a download> click fires navigate event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-download.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-download.html
new file mode 100644
index 0000000000..05fb0ecf2f
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-download.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+const tests = [["a", ""], ["a", "filename"], ["area", ""], ["area", "filename"]];
+
+for (const [tag, download_attr] of tests) {
+ async_test(t => {
+ let a = document.createElement(tag);
+ a.href = "foo.html";
+ a.download = download_attr;
+ document.body.appendChild(a);
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.downloadRequest, download_attr);
+ assert_equals(e.formData, null);
+ assert_equals(new URL(e.destination.url).pathname,
+ "/navigation-api/navigate-event/foo.html");
+ assert_false(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ e.preventDefault();
+ });
+ a.click();
+ }, `<${tag}> fires navigate and populates downloadRequest with '${download_attr}'`);
+}
+</script>
+</body>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-fragment.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-fragment.html
new file mode 100644
index 0000000000..51221ebcad
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-fragment.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<a id="a" href="#1"></a>
+<script>
+async_test(t => {
+ navigation.onnavigate = t.step_func(e => {
+ assert_equals(e.navigationType, "push");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_true(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(e.formData, null);
+ assert_equals(new URL(e.destination.url).hash, "#1");
+ assert_true(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ e.preventDefault();
+ t.step_timeout(t.step_func_done(() => assert_equals(location.hash, "")), 0);
+ });
+ a.click();
+}, "Fragment <a> click fires navigate event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-same-origin-cross-document.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-same-origin-cross-document.html
new file mode 100644
index 0000000000..68f5bf0627
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-same-origin-cross-document.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<a id="a" href="foo.html"></a>
+<script>
+async_test(t => {
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.formData, null);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).pathname,
+ "/navigation-api/navigate-event/foo.html");
+ assert_false(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ e.preventDefault();
+ });
+ a.click();
+}, "<a> cross-document (but same-origin) navigate event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-userInitiated.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-userInitiated.html
new file mode 100644
index 0000000000..39192c9151
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-userInitiated.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<a id="a" href="#1">Click me</a>
+<script>
+async_test(t => {
+ navigation.onnavigate = t.step_func(e => {
+ assert_equals(e.navigationType, "push");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_true(e.userInitiated);
+ assert_true(e.hashChange);
+ assert_equals(e.formData, null);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).hash, "#1");
+ assert_true(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ e.preventDefault();
+ t.step_timeout(t.step_func_done(() => assert_equals(location.hash, "")), 0);
+ });
+
+ test_driver.click(a);
+}, "Fragment <a> click fires navigate event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-with-target.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-with-target.html
new file mode 100644
index 0000000000..6407b963be
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-anchor-with-target.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="iframe" name="i" src="/common/blank.html"></iframe>
+<a id="a" href="foo.html" target="i"></a>
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ navigation.onnavigate = t.step_func_done(() => {
+ assert_unreached("onnavigate should not have fired in source window");
+ });
+ iframe.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.formData, null);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).pathname,
+ "/navigation-api/navigate-event/foo.html");
+ assert_false(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ e.preventDefault();
+ });
+ a.click();
+ });
+}, "<a> with a target fires navigate event in target window but not source");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-after-detach.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-after-detach.html
new file mode 100644
index 0000000000..1dcb6cac43
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-after-detach.html
@@ -0,0 +1,33 @@
+<!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));
+
+ let destination_key = i.contentWindow.navigation.currentEntry.key;
+ let destination_id = i.contentWindow.navigation.currentEntry.id;
+ let destination_index = i.contentWindow.navigation.currentEntry.index;
+ await i.contentWindow.navigation.navigate("#1").finished;
+
+ let back_destination;
+ i.contentWindow.navigation.onnavigate = e => back_destination = e.destination;
+ await i.contentWindow.navigation.back().finished;
+
+ // Before detach, key/id/index are valid.
+ assert_equals(back_destination.key, destination_key);
+ assert_equals(back_destination.id, destination_id);
+ assert_equals(back_destination.index, destination_index);
+
+ i.remove();
+
+ // After detach, key/id/index are invalid, but the url is still valid.
+ assert_equals(back_destination.key, "");
+ assert_equals(back_destination.id, "");
+ assert_equals(new URL(back_destination.url).pathname, "/common/blank.html");
+ assert_equals(back_destination.index, -1);
+}, "navigate event destination after iframe detach");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-dynamic-index.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-dynamic-index.html
new file mode 100644
index 0000000000..2e0f1ea497
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-dynamic-index.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../navigation-methods/return-value/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));
+ await navigation.navigate("#1").finished;
+
+ let back_destination;
+ navigation.addEventListener("navigate", t.step_func(e => {
+ back_destination = e.destination;
+ assert_equals(back_destination.index, 0);
+ }), { once: true });
+ await navigation.back().finished;
+
+ // Disposing the destination entry of back_destination should update
+ // back_destination.index, even though back_destination's navigation has
+ // completed.
+ await navigation.navigate("#clobber_back", { history: "replace" }).finished;
+ assert_equals(back_destination.index, -1);
+
+ navigation.addEventListener("navigate", t.step_func(e => {
+ assert_equals(e.destination.index, 1);
+
+ // Dispose the destination entry and destination.index should update.
+ navigation.navigate("#clobber_forward");
+ assert_equals(e.destination.index, -1);
+ }), { once: true });
+ await assertBothRejectDOM(t, navigation.forward(), "AbortError");
+}, "navigate event destination.index should be dynamic");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-getState-back-forward.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-getState-back-forward.html
new file mode 100644
index 0000000000..c8b1043aba
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-getState-back-forward.html
@@ -0,0 +1,26 @@
+<!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(() => {
+ let navState = { statevar: "state" };
+ navigation.navigate("#foo", { history: "replace", state: navState });
+ let target_key = navigation.currentEntry.key;
+ let target_id = navigation.currentEntry.id;
+ navigation.navigate("#bar");
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "traverse");
+ assert_not_equals(e.destination, null);
+ assert_not_equals(e.destination.getState(), undefined);
+ assert_not_equals(e.destination.getState(), e.destination.getState());
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ });
+ navigation.back();
+ }), 0);
+}, "navigate event destination.getState() on back/forward navigation");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-getState-navigate.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-getState-navigate.html
new file mode 100644
index 0000000000..5dac40de56
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-getState-navigate.html
@@ -0,0 +1,23 @@
+<!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(() => {
+ let navState = { statevar: "state" };
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push");
+ assert_not_equals(e.destination, null);
+ assert_not_equals(e.destination.getState(), undefined);
+ assert_equals(e.destination.getState().statevar, "state");
+ assert_not_equals(e.destination.getState(), e.destination.getState());
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ });
+ navigation.navigate("#foo", { state: navState });
+ }, 0);
+}, "navigate event destination.getState() should be the state given to navigate()");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-getState-reload.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-getState-reload.html
new file mode 100644
index 0000000000..a180e086a9
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-destination-getState-reload.html
@@ -0,0 +1,25 @@
+<!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(() => {
+ let navState = { statevar: "state" };
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "reload");
+ assert_not_equals(e.destination, null);
+ assert_not_equals(e.destination.getState(), undefined);
+ assert_equals(e.destination.getState().statevar, "state");
+ assert_not_equals(e.destination.getState(), e.destination.getState());
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ e.intercept();
+ });
+ navigation.updateCurrentEntry({ state: navState });
+ location.reload();
+ }, 0);
+}, "navigate event destination.getState() on location.reload()");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-get.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-get.html
new file mode 100644
index 0000000000..87a102ddc0
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-get.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<form id="form" action=""></form>
+<script>
+async_test(t => {
+ navigation.onnavigate = t.step_func_done(e => {
+ e.preventDefault();
+
+ assert_equals(e.navigationType, "replace");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(e.destination.url, location.href + "?");
+ assert_false(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+
+ // Because it's a GET, not a POST
+ assert_equals(e.formData, null);
+ });
+ window.onload = t.step_func(() => form.submit());
+}, "<form> submission with GET method fires navigate event but with formData null");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-reload.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-reload.html
new file mode 100644
index 0000000000..f18a11ebda
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-reload.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="iframe" name="i" src="/common/blank.html"></iframe>
+<form id="form" action="/common/blank.html?1" method="post" target="i"></form>
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ navigation.onnavigate = t.step_func_done(() => {
+ assert_unreached("onnavigate should not have fired in source window");
+ });
+ iframe.contentWindow.navigation.onnavigate = t.step_func(e => {
+ assert_equals(e.navigationType, "push");
+ assert_not_equals(e.formData, null);
+
+ iframe.onload = t.step_func(() => {
+ iframe.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "reload");
+ assert_equals(e.formData, null);
+ });
+
+ iframe.contentWindow.location.reload();
+ });
+ });
+ form.submit();
+ });
+}, "reloading a page created from form submission results in formData of null, not the original form data");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-traverse.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-traverse.html
new file mode 100644
index 0000000000..d673537503
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-traverse.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="iframe" name="i" src="/common/blank.html"></iframe>
+<form id="form" action="/common/blank.html?1" method="post" target="i"></form>
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ navigation.onnavigate = t.step_func_done(() => {
+ assert_unreached("onnavigate should not have fired in source window");
+ });
+ iframe.contentWindow.navigation.onnavigate = t.step_func(e => {
+ assert_equals(e.navigationType, "push");
+ assert_not_equals(e.formData, null);
+
+ iframe.onload = t.step_func(() => {
+ // Avoid the replace behavior that occurs if you navigate during the load handler
+ t.step_timeout(() => {
+ iframe.contentWindow.navigation.onnavigate = t.step_func(e => {
+ assert_equals(e.navigationType, "push");
+ assert_equals(e.formData, null);
+ });
+
+ iframe.contentWindow.onhashchange = t.step_func(() => {
+ iframe.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "traverse");
+ assert_equals(e.formData, null);
+ });
+
+ // 3: go back
+ iframe.contentWindow.history.back();
+ });
+
+ // 2: navigate from /common/blank.html?1-with-form-data to /common/blank.html?1#1-with-form-data
+ iframe.contentWindow.location.hash = "#1";
+ }, 0);
+ });
+ });
+
+ // 1: submit the form, navigating from /common/blank.html to /common/blank.html?1-with-form-data
+ form.submit();
+ });
+}, "reloading a page created from form submission results in formData of null, not the original form data");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-userInitiated.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-userInitiated.html
new file mode 100644
index 0000000000..40c5905447
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-userInitiated.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<form id="form" method="post" action="">
+<input id="submit" type="submit" value="Submit">
+</form>
+<script>
+async_test(t => {
+ navigation.onnavigate = t.step_func_done(e => {
+ e.preventDefault();
+
+ assert_equals(e.navigationType, "push");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_true(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(e.destination.url, location.href);
+ assert_false(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ assert_not_equals(e.formData, null);
+ });
+ window.onload = t.step_func(() => test_driver.click(submit));
+}, "<form> submission fires navigate event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-with-target.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-with-target.html
new file mode 100644
index 0000000000..f6fe05c938
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-form-with-target.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="iframe" name="i" src="/common/blank.html"></iframe>
+<form id="form" method="post" action="foo.html" target="i"></form>
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ navigation.onnavigate = t.unreached_func("onnavigate should not have fired in source window");
+
+ iframe.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).pathname,
+ "/navigation-api/navigate-event/foo.html");
+ assert_false(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ assert_not_equals(e.formData, null);
+ });
+ form.submit();
+ });
+}, "<form> submission with a target fires navigate event in target window but not source");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-form.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-form.html
new file mode 100644
index 0000000000..c57d72c3de
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-form.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<form id="form" method="post" action=""></form>
+<script>
+async_test(t => {
+ navigation.onnavigate = t.step_func_done(e => {
+ e.preventDefault();
+
+ assert_equals(e.navigationType, "replace");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(e.destination.url, location.href);
+ assert_false(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ assert_not_equals(e.formData, null);
+ });
+ window.onload = t.step_func(() => form.submit());
+}, "<form> submission fires navigate event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-after-fragment.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-after-fragment.html
new file mode 100644
index 0000000000..57a30c85b8
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-after-fragment.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ window.onload = () => t.step_timeout(() => {
+ let start_length = history.length;
+ let target_key = navigation.currentEntry.key;
+ let target_id = navigation.currentEntry.id;
+ location.hash = "#1";
+ assert_equals(history.length, start_length + 1);
+
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "traverse");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_true(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).hash, "");
+ assert_true(e.destination.sameDocument);
+ assert_equals(e.destination.key, target_key);
+ assert_equals(e.destination.id, target_id);
+ assert_equals(e.destination.index, 0);
+ assert_equals(e.formData, null);
+ });
+
+ history.back();
+ }, 0);
+}, "history.back() fires the navigate event and sets hashChange when reversing a fragment navigation");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-after-pushState.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-after-pushState.html
new file mode 100644
index 0000000000..bf2e6e4e30
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-after-pushState.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ window.onload = () => t.step_timeout(() => {
+ let start_length = history.length;
+ let target_key = navigation.currentEntry.key;
+ let target_id = navigation.currentEntry.id;
+ history.pushState(1, "", "pushState.html");
+ assert_equals(history.length, start_length + 1);
+
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "traverse");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).hash, "");
+ assert_true(e.destination.sameDocument);
+ assert_equals(e.destination.key, target_key);
+ assert_equals(e.destination.id, target_id);
+ assert_equals(e.destination.index, 0);
+ assert_equals(e.formData, null);
+ });
+
+ history.back();
+ }, 0);
+}, "history.back() fires the navigate event when reversing a pushState()");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-cross-document.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-cross-document.html
new file mode 100644
index 0000000000..cd7be6e9ad
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-cross-document.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 => {
+ window.onload = t.step_func(() => {
+ let target_key = i.contentWindow.navigation.currentEntry.key;
+ let target_id = i.contentWindow.navigation.currentEntry.id;
+ i.contentWindow.navigation.navigate("?foo");
+ i.onload = t.step_func(() => {
+ i.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "traverse");
+ assert_false(e.cancelable);
+ assert_false(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).pathname, "/common/blank.html");
+ assert_false(e.destination.sameDocument);
+ assert_equals(e.destination.key, target_key);
+ assert_equals(e.destination.id, target_id);
+ assert_equals(e.destination.index, 0);
+ assert_equals(e.formData, null);
+ assert_equals(e.info, undefined);
+ });
+ assert_true(i.contentWindow.navigation.canGoBack);
+ i.contentWindow.history.back();
+ })
+ });
+}, "navigate event for history.back() - cross-document");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-noop.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-noop.html
new file mode 100644
index 0000000000..10763c93fd
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-back-noop.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>
+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 i.contentWindow.navigation.navigate("#").finished;
+ assert_equals(i.contentWindow.navigation.entries().length, 2);
+ assert_equals(i.contentWindow.navigation.currentEntry.index, 1);
+ assert_equals(navigation.entries().length, 1);
+ assert_equals(navigation.currentEntry.index, 0);
+ assert_equals(history.length, 2);
+
+ i.remove();
+ assert_equals(navigation.entries().length, 1);
+ assert_equals(navigation.currentEntry.index, 0);
+ assert_equals(history.length, 2);
+
+ // back() here should do nothing. The iframe that would have navigated has
+ // been removed. No navigate event should be fired.
+ navigation.onnavigate = t.unreached_func("navigate must not fire");
+ navigation.oncurrententrychange = t.unreached_func("currententrychange must not fire");
+ history.back();
+
+ // Give time for the navigation to proceed.
+ await new Promise(resolve => t.step_timeout(resolve, 20));
+}, "history.back() does not fire a navigate event when there's nothing to navigate");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-go-0.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-go-0.html
new file mode 100644
index 0000000000..b1f41425b6
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-go-0.html
@@ -0,0 +1,27 @@
+<!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.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "reload");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).pathname, "/common/blank.html");
+ assert_false(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ assert_equals(e.formData, null);
+ e.preventDefault();
+ });
+
+ i.contentWindow.history.go(0);
+ });
+}, "history.go(0) fires the navigate event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-pushState.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-pushState.html
new file mode 100644
index 0000000000..266309a79e
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-pushState.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ let start_length = history.length;
+ navigation.onnavigate = t.step_func(e => {
+ assert_equals(e.navigationType, "push");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).hash, "#1");
+ assert_true(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ assert_equals(e.formData, null);
+ e.preventDefault();
+ t.step_timeout(t.step_func_done(() => {
+ assert_equals(location.hash, "");
+ assert_equals(history.state, null);
+ assert_equals(history.length, start_length);
+ }), 0);
+ });
+ history.pushState(1, null, "#1");
+}, "history.pushState() fires the navigate event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-replaceState.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-replaceState.html
new file mode 100644
index 0000000000..ea6d3df455
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-history-replaceState.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ let start_length = history.length;
+ navigation.onnavigate = t.step_func(e => {
+ assert_equals(e.navigationType, "replace");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).hash, "#1");
+ assert_true(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ assert_equals(e.formData, null);
+ e.preventDefault();
+ t.step_timeout(t.step_func_done(() => {
+ assert_equals(location.hash, "");
+ assert_equals(history.state, null);
+ assert_equals(history.length, start_length);
+ }), 0);
+ });
+ history.replaceState(1, null, "#1");
+}, "history.replaceState() fires the navigate event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-iframe-location.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-iframe-location.html
new file mode 100644
index 0000000000..25d51476f3
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-iframe-location.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="iframe" src="/common/blank.html"></iframe>
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ navigation.onnavigate = t.step_func_done(() => {
+ assert_unreached("onnavigate should not have fired in source window");
+ });
+ iframe.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_true(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).hash, "#1");
+ assert_true(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ assert_equals(e.formData, null);
+ e.preventDefault();
+ t.step_timeout(t.step_func_done(() => assert_equals(location.hash, "")), 0);
+ });
+ iframe.contentWindow.location.hash = "#1";
+ });
+}, "location API on another window fires navigate event in the target window only");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-location.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-location.html
new file mode 100644
index 0000000000..a4d0c60776
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-location.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<form id="form" action=""></form>
+<script>
+async_test(t => {
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "replace");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_true(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).hash, "#1");
+ assert_true(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ assert_equals(e.formData, null);
+ });
+ location.href = "#1";
+}, "location API fires navigate event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-meta-refresh.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-meta-refresh.html
new file mode 100644
index 0000000000..9fa59b29f2
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-meta-refresh.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="resources/meta-refresh.html"></iframe>
+</head>
+<script>
+async_test(t => {
+ i.onload = t.step_func(() => {
+ i.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "reload");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(e.destination.url, i.contentWindow.location.href);
+ assert_false(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ assert_equals(e.formData, null);
+ e.preventDefault();
+ });
+ });
+}, "<meta> refresh fires navigate event");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-back-cross-document.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-back-cross-document.html
new file mode 100644
index 0000000000..2e1adbeee9
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-back-cross-document.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 => {
+ window.onload = t.step_func(() => {
+ let target_key = i.contentWindow.navigation.currentEntry.key;
+ let target_id = i.contentWindow.navigation.currentEntry.id;
+ i.contentWindow.navigation.navigate("?foo");
+ i.onload = t.step_func(() => {
+ let beforeunload_called = false;
+ i.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_true(beforeunload_called);
+ assert_equals(e.navigationType, "traverse");
+ assert_false(e.cancelable);
+ assert_false(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_false(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).pathname, "/common/blank.html");
+ assert_false(e.destination.sameDocument);
+ assert_equals(e.destination.key, target_key);
+ assert_equals(e.destination.id, target_id);
+ assert_equals(e.destination.index, 0);
+ assert_equals(e.formData, null);
+ assert_equals(e.info, "hi");
+ });
+ i.contentWindow.onbeforeunload = () => beforeunload_called = true;
+ assert_true(i.contentWindow.navigation.canGoBack);
+ i.contentWindow.navigation.back({ info: "hi" });
+ })
+ });
+}, "navigate event for navigation.back() - cross-document");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-back-same-document-in-iframe.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-back-same-document-in-iframe.html
new file mode 100644
index 0000000000..cebd2f3693
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-back-same-document-in-iframe.html
@@ -0,0 +1,33 @@
+<!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));
+
+ let target_key = i.contentWindow.navigation.currentEntry.key;
+ let target_id = i.contentWindow.navigation.currentEntry.id;
+ await i.contentWindow.navigation.navigate("#").finished;
+ assert_true(i.contentWindow.navigation.canGoBack);
+
+ i.contentWindow.navigation.onnavigate = e => {
+ assert_equals(e.navigationType, "traverse");
+ assert_false(e.cancelable, "traversals in iframes should never be cancelable");
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_true(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).hash, "");
+ assert_true(e.destination.sameDocument);
+ assert_equals(e.destination.key, target_key);
+ assert_equals(e.destination.id, target_id);
+ assert_equals(e.destination.index, 0);
+ assert_equals(e.formData, null);
+ assert_equals(e.info, "hi");
+ }
+ await i.contentWindow.navigation.back({ info: "hi" }).finished;
+}, "navigate event for navigation.back() - same-document in an iframe");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-back-same-document.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-back-same-document.html
new file mode 100644
index 0000000000..431d38449c
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-back-same-document.html
@@ -0,0 +1,32 @@
+<!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(() => {
+ let target_key = navigation.currentEntry.key;
+ let target_id = navigation.currentEntry.id;
+ navigation.navigate("#foo").committed.then(t.step_func(() => {
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "traverse");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_true(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).hash, "");
+ assert_true(e.destination.sameDocument);
+ assert_equals(e.destination.key, target_key);
+ assert_equals(e.destination.id, target_id);
+ assert_equals(e.destination.index, 0);
+ assert_equals(e.formData, null);
+ assert_equals(e.info, "hi");
+ });
+ assert_true(navigation.canGoBack);
+ navigation.back({ info: "hi" });
+ }));
+ }, 0);
+}, "navigate event for navigation.back() - same-document");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-navigate.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-navigate.html
new file mode 100644
index 0000000000..ffc8ea867f
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-navigation-navigate.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "replace");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_true(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).hash, "#foo");
+ assert_true(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ assert_equals(e.formData, null);
+ });
+ navigation.navigate("#foo");
+}, "navigate event for navigation.navigate()");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-to-javascript.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-to-javascript.html
new file mode 100644
index 0000000000..78f490d87b
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-to-javascript.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="iframe" name="i" src="/common/blank.html"></iframe>
+
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ navigation.onnavigate = t.unreached_func("onnavigate should not have fired in source window");
+
+ iframe.contentWindow.navigation.onnavigate = t.unreached_func("onnavigate should not have fired in iframe window");
+
+ iframe.contentWindow.location.href = "javascript:'foo'";
+
+ iframe.onload = () => t.done();
+ });
+}, "navigate event does not fire for javascript: URL navigations");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-to-srcdoc.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-to-srcdoc.html
new file mode 100644
index 0000000000..8bbb66a31f
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-to-srcdoc.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="iframe" name="i" src="/common/blank.html"></iframe>
+
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ navigation.onnavigate = t.unreached_func("onnavigate should not have fired in source window");
+
+ iframe.contentWindow.navigation.onnavigate = t.step_func(e => {
+ assert_equals(e.navigationType, "push");
+ assert_true(e.cancelable, "cancelable");
+ assert_false(e.canIntercept, "canIntercept");
+ assert_false(e.userInitiated, "userInitiated");
+ assert_false(e.hashChange, "hashChange");
+ assert_equals(e.downloadRequest, null);
+ assert_equals(e.destination.url, "about:srcdoc");
+ assert_false(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ assert_equals(e.formData, null);
+ e.preventDefault();
+
+ // Make sure it doesn't navigate anyway.
+ iframe.onload = t.unreached_func("Must not load the srcdoc document");
+ t.step_timeout(() => t.done(), 10);
+ });
+
+ iframe.srcdoc = "srcdoc contents";
+ });
+}, "navigate event fires appropriately (and can be canceled) for adding the srcdoc attribute");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-window-open-self.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-window-open-self.html
new file mode 100644
index 0000000000..a6e443fd5a
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-window-open-self.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_true(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).hash, "#1");
+ assert_true(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ assert_equals(e.formData, null);
+ e.preventDefault();
+ });
+ window.onload = t.step_func(() => window.open("#1", "_self"));
+}, "window.open() fires navigate event when targeting self");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigate-window-open.html b/testing/web-platform/tests/navigation-api/navigate-event/navigate-window-open.html
new file mode 100644
index 0000000000..1fe2402bc9
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigate-window-open.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="iframe" name="i" src="/common/blank.html"></iframe>
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ navigation.onnavigate = t.step_func_done(() => {
+ assert_unreached("onnavigate should not have fired in source window");
+ });
+ iframe.contentWindow.navigation.onnavigate = t.step_func_done(e => {
+ assert_equals(e.navigationType, "push");
+ assert_true(e.cancelable);
+ assert_true(e.canIntercept);
+ assert_false(e.userInitiated);
+ assert_true(e.hashChange);
+ assert_equals(e.downloadRequest, null);
+ assert_equals(new URL(e.destination.url).hash, "#1");
+ assert_true(e.destination.sameDocument);
+ assert_equals(e.destination.key, "");
+ assert_equals(e.destination.id, "");
+ assert_equals(e.destination.index, -1);
+ assert_equals(e.formData, null);
+ e.preventDefault();
+ });
+
+ window.open("/common/blank.html#1", "i");
+ });
+}, "window.open() fires navigate event in target window but not source");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigatesuccess-cross-document.html b/testing/web-platform/tests/navigation-api/navigate-event/navigatesuccess-cross-document.html
new file mode 100644
index 0000000000..1d528c1f5f
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigatesuccess-cross-document.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="resources/navigatesuccess-cross-document-helper.html"></iframe>
+<script>
+async_test(t => {
+ // The iframe will post a message if it receives a navigatesuccess.
+ window.onmessage = t.unreached_func("navigatesuccess received");
+ window.onload = t.step_func(() => {
+ i.contentWindow.location.search = "?1";
+ i.onload = t.step_func_done(() => assert_equals(i.contentWindow.location.search, "?1"));
+ });
+}, "navigatesuccess does not fire for a cross-document navigation");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigatesuccess-same-document.html b/testing/web-platform/tests/navigation-api/navigate-event/navigatesuccess-same-document.html
new file mode 100644
index 0000000000..6007170ec1
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigatesuccess-same-document.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<a id="a" href="#1"></a>
+<script>
+async_test(t => {
+ navigation.onnavigatesuccess = t.step_func_done(() => assert_equals(location.hash, "#1"));
+ a.click();
+}, "navigatesuccess fires for a same-document navigation");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigation-back-cross-document-preventDefault.html b/testing/web-platform/tests/navigation-api/navigate-event/navigation-back-cross-document-preventDefault.html
new file mode 100644
index 0000000000..0b5b750876
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigation-back-cross-document-preventDefault.html
@@ -0,0 +1,33 @@
+
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../navigation-methods/return-value/resources/helpers.js"></script>
+<script src="/common/get-host-info.sub.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.
+ let w = window.open("resources/opener-postMessage-onload.html");
+ await new Promise(resolve => window.onmessage = resolve);
+ // Navigate to a url that will notify us when the navigation is complete.
+ w.navigation.navigate("opener-postMessage-onload.html?1");
+
+ await new Promise(resolve => window.onmessage = resolve);
+ assert_equals(w.navigation.entries().length, 2);
+ assert_equals(w.navigation.currentEntry.index, 1);
+ let navigate_called = false;
+ w.navigation.onnavigate = t.step_func(e => {
+ navigate_called = true;
+ assert_false(e.destination.sameDocument);
+ assert_false(e.cancelable);
+ // Should do nothing.
+ e.preventDefault();
+ });
+ w.navigation.back();
+ await new Promise(resolve => window.onmessage = resolve);
+ assert_equals(w.navigation.currentEntry.index, 0);
+ assert_true(navigate_called);
+}, "navigation.back() cross-document cannot be cancelled with the navigate event");
+</script>
+
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigation-back-same-document-preventDefault.html b/testing/web-platform/tests/navigation-api/navigate-event/navigation-back-same-document-preventDefault.html
new file mode 100644
index 0000000000..7edb188823
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigation-back-same-document-preventDefault.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../navigation-methods/return-value/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));
+
+ await navigation.navigate("#").finished;
+ assert_equals(navigation.entries().length, 2);
+ assert_equals(navigation.currentEntry.index, 1);
+
+ navigation.onnavigate = e => e.preventDefault();
+
+ navigation.onnavigateerror = t.step_func(e => {
+ assert_equals(e.constructor, ErrorEvent);
+ assert_equals(e.filename, location.href);
+ navigateerror_called = true;
+ });
+ await assertBothRejectDOM(t, navigation.back(), "AbortError");
+ assert_equals(navigation.currentEntry.index, 1);
+ assert_true(navigateerror_called);
+}, "navigation.back() same-document preventDefault");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-in-iframe-same-document-preventDefault.html b/testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-in-iframe-same-document-preventDefault.html
new file mode 100644
index 0000000000..463746e8f7
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-in-iframe-same-document-preventDefault.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../navigation-methods/return-value/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));
+
+ // Navigate the iframe, then the top window, so that when the iframe goes back
+ // to its initial entry, the top window navigates as well.
+ await i.contentWindow.navigation.navigate("#").finished;
+ await navigation.navigate("#").finished;
+ 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);
+
+ // Ensure the top window, which is allowed to cancel the traversal, does so.
+ navigation.onnavigate = e => e.preventDefault();
+
+ let top_navigateerror_fired = false;
+ navigation.onnavigateerror = t.step_func(e => {
+ assert_equals(e.constructor, ErrorEvent);
+ assert_equals(e.filename, location.href);
+ top_navigateerror_fired = true;
+ });
+
+ i.contentWindow.navigation.onnavigate = t.unreached_func("navigate event should not fire in the iframe, because the traversal was cancelled in the top window");
+ i.contentWindow.navigation.onnavigateerror = t.unreached_func("navigateerror event should not fire in the iframe, because the navigate event was not fired");
+
+ // When the top window blocks the traversal, it should be blocked in the
+ // iframe as well, and the traversal promises in the iframe should be rejected.
+ const iWindow = i.contentWindow;
+ const iDOMException = iWindow.DOMException;
+ await assertBothRejectDOM(t, i.contentWindow.navigation.traverseTo(i.contentWindow.navigation.entries()[0].key), "AbortError", iWindow, iDOMException);
+ assert_true(top_navigateerror_fired);
+ assert_equals(navigation.currentEntry.index, 1);
+ assert_equals(i.contentWindow.navigation.currentEntry.index, 1);
+}, "navigation.traverseTo() in an iframe with same-document preventDefault in its parent");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-navigates-top-and-same-doc-child-and-cross-doc-child.html b/testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-navigates-top-and-same-doc-child-and-cross-doc-child.html
new file mode 100644
index 0000000000..31cb54fca2
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-navigates-top-and-same-doc-child-and-cross-doc-child.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i1" src="/common/blank.html"></iframe>
+<iframe id="i2" 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));
+ await navigation.navigate("#").finished;
+ await i1.contentWindow.navigation.navigate("#").finished;
+ i2.contentWindow.navigation.navigate("?");
+ await new Promise(resolve => i2.onload = () => t.step_timeout(resolve, 0));
+
+ assert_equals(navigation.entries().length, 2);
+ assert_equals(i1.contentWindow.navigation.entries().length, 2);
+ assert_equals(i2.contentWindow.navigation.entries().length, 2);
+ assert_equals(navigation.currentEntry.index, 1);
+ assert_equals(i1.contentWindow.navigation.currentEntry.index, 1);
+ assert_equals(i2.contentWindow.navigation.currentEntry.index, 1);
+
+ let navigate_event_count = 0;
+ navigation.onnavigate = t.step_func(e => {
+ assert_equals(navigate_event_count, 0);
+ navigate_event_count++;
+ assert_true(e.cancelable);
+ });
+ i1.contentWindow.navigation.onnavigate = t.step_func(e => {
+ assert_true(navigate_event_count > 0);
+ navigate_event_count++;
+ assert_false(e.cancelable);
+ });
+ i2.contentWindow.navigation.onnavigate = t.step_func(e => {
+ assert_true(navigate_event_count > 0);
+ navigate_event_count++;
+ assert_false(e.cancelable);
+ });
+
+ await navigation.traverseTo(navigation.entries()[0].key).finished;
+ // The top window will finish quickly, becuase it is same-document traversal.
+ // i2 will be slower because it is cross-document, so wait for its onload.
+ await new Promise(resolve => i2.onload = () => t.step_timeout(resolve, 0));
+ assert_equals(navigate_event_count, 3);
+ assert_equals(navigation.currentEntry.index, 0);
+ assert_equals(i1.contentWindow.navigation.currentEntry.index, 0);
+ assert_equals(i2.contentWindow.navigation.currentEntry.index, 0);
+}, "navigation.traverseTo() can navigate 3 frames of different types with correct navigate event cancelable values");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-same-document-preventDefault-multiple-windows.html b/testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-same-document-preventDefault-multiple-windows.html
new file mode 100644
index 0000000000..9bb64fb7cc
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-same-document-preventDefault-multiple-windows.html
@@ -0,0 +1,23 @@
+<!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));
+ await navigation.navigate("#").finished;
+ await i.contentWindow.navigation.navigate("#").finished;
+ 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);
+
+ navigation.onnavigate = e => e.preventDefault();
+ i.contentWindow.navigation.onnavigate = t.unreached_func("navigate event should not fire in the iframe, because the traversal was cancelled in the top window");
+ await promise_rejects_dom(t, "AbortError", navigation.traverseTo(navigation.entries()[0].key).finished);
+ assert_equals(navigation.currentEntry.index, 1);
+ assert_equals(i.contentWindow.navigation.currentEntry.index, 1);
+}, "navigation.traverseTo() - if a top window cancels the traversal, any iframes should not fire navigate");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-top-cancels-cross-document-child.html b/testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-top-cancels-cross-document-child.html
new file mode 100644
index 0000000000..11f07afefc
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/navigation-traverseTo-top-cancels-cross-document-child.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../navigation-methods/return-value/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));
+ await navigation.navigate("#").finished;
+ i.contentWindow.navigation.navigate("?");
+ await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));
+
+ 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);
+
+ navigation.onnavigate = t.step_func(e => e.preventDefault());
+ i.contentWindow.navigation.onnavigate = t.unreached_func("navigation should be cancelled before iframe fires navigate event");
+ await assertBothRejectDOM(t, navigation.traverseTo(navigation.entries()[0].key), "AbortError");
+ // Give the iframe time to navigate in case it was incorrectly permitted.
+ await new Promise(resolve => t.step_timeout(resolve, 50));
+}, "navigate.traverseTo() cancelled by top frame cancels cross-document iframe");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/replaceState-in-unload-then-remove-iframe.html b/testing/web-platform/tests/navigation-api/navigate-event/replaceState-in-unload-then-remove-iframe.html
new file mode 100644
index 0000000000..e97b72b157
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/replaceState-in-unload-then-remove-iframe.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.onunload = t.step_func(() => {
+ i.contentWindow.history.replaceState(null, "", "#");
+ i.remove();
+ t.step_timeout(t.step_func_done(), 0);
+ });
+ i.contentWindow.location = "/common/blank.html?1";
+ });
+}, "reacting to the navigate event doesn't crash when replaceState is called in onunload");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/replaceState-inside-back-handler.html b/testing/web-platform/tests/navigation-api/navigate-event/replaceState-inside-back-handler.html
new file mode 100644
index 0000000000..29409b5e2b
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/replaceState-inside-back-handler.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../navigation-methods/return-value/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));
+
+ await navigation.navigate("#push").finished;
+ navigation.onnavigate = () => history.replaceState(null, "", "#");
+ await assertBothRejectDOM(t, navigation.back(), "AbortError");
+}, "replaceState inside a navigate event for navigation.back()");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/resources/meta-refresh.html b/testing/web-platform/tests/navigation-api/navigate-event/resources/meta-refresh.html
new file mode 100644
index 0000000000..fd453e663f
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/resources/meta-refresh.html
@@ -0,0 +1,4 @@
+<head>
+<meta http-equiv="refresh" content="0"></meta>
+</head>
+<body></body>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/resources/navigatesuccess-cross-document-helper.html b/testing/web-platform/tests/navigation-api/navigate-event/resources/navigatesuccess-cross-document-helper.html
new file mode 100644
index 0000000000..aabc5015a9
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/resources/navigatesuccess-cross-document-helper.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<head>
+<script>
+navigation.onnavigatesuccess = () => top.postMessage("navigatesuccess received");
+</script>
+</head>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/resources/opener-postMessage-onload.html b/testing/web-platform/tests/navigation-api/navigate-event/resources/opener-postMessage-onload.html
new file mode 100644
index 0000000000..97e1d82058
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/resources/opener-postMessage-onload.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<head>
+<script>
+window.onload = () => opener.postMessage("onload", "*");
+</script>
+</head>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/same-url-replace-cross-document.html b/testing/web-platform/tests/navigation-api/navigate-event/same-url-replace-cross-document.html
new file mode 100644
index 0000000000..0a976cd51f
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/same-url-replace-cross-document.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 we are definitely testing the
+ // same URL as the cause of the rejections.
+ await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));
+ assert_equals(navigation.entries().length, 1);
+
+ navigation.onnavigate = t.step_func(e => {
+ e.intercept();
+ assert_equals(e.navigationType, "replace");
+ });
+ navigation.onnavigateerror = t.unreached_func("onnavigateerror should not be called");
+
+ await navigation.navigate(location.href).finished;
+ assert_equals(navigation.entries().length, 1);
+ assert_equals(navigation.currentEntry.index, 0);
+}, "navigate() to the current URL cross document should replace");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/same-url-replace-same-document.html b/testing/web-platform/tests/navigation-api/navigate-event/same-url-replace-same-document.html
new file mode 100644
index 0000000000..839c687934
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/same-url-replace-same-document.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 we are definitely testing the
+ // same URL as the cause of the rejections.
+ await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));
+ assert_equals(navigation.entries().length, 1);
+ await navigation.navigate("#").finished;
+ assert_equals(navigation.entries().length, 2);
+ assert_equals(navigation.currentEntry.index, 1);
+
+ navigation.onnavigate = t.step_func(e => {
+ assert_equals(e.navigationType, "replace");
+ });
+ await navigation.navigate(location.href).finished;
+ assert_equals(navigation.entries().length, 2);
+ assert_equals(navigation.currentEntry.index, 1);
+}, "navigate() to the current URL same document should replace");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-detach-in-onnavigate.html b/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-detach-in-onnavigate.html
new file mode 100644
index 0000000000..467ea88899
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-detach-in-onnavigate.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>
+<script>
+promise_test(async t => {
+ await new Promise(resolve => i.onload = resolve);
+ let iframe_constructor = i.contentWindow.DOMException;
+ let iframe_typeerror = i.contentWindow.TypeError;
+ let abort_signal;
+ let onabort_called = false;
+ i.contentWindow.navigation.onnavigate = t.step_func(e => {
+ abort_signal = e.signal;
+ abort_signal.onabort = () => onabort_called = true;
+ i.remove();
+ });
+ await promise_rejects_dom(t, 'AbortError', iframe_constructor, i.contentWindow.navigation.navigate("#1").committed);
+ assert_true(abort_signal.aborted);
+ assert_true(onabort_called);
+}, "window detach inside a navigate event signals event.signal");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-intercept.html b/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-intercept.html
new file mode 100644
index 0000000000..1e92d8e445
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-intercept.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 abort_signal;
+ let onabort_called = false;
+ navigation.onnavigate = t.step_func(e => {
+ abort_signal = e.signal;
+ abort_signal.onabort = () => onabort_called = true;
+ e.intercept();
+ });
+
+ await navigation.navigate("?1").finished;
+ assert_false(abort_signal.aborted);
+ assert_false(onabort_called);
+}, "event.intercept() does not signal event.signal");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-preventDefault.html b/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-preventDefault.html
new file mode 100644
index 0000000000..60fed90ce6
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-preventDefault.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+promise_test(async t => {
+ let abort_signal;
+ let events = [];
+ navigation.onnavigateerror = () => events.push("onnavigateerror");
+ navigation.onnavigate = t.step_func(e => {
+ abort_signal = e.signal;
+ abort_signal.onabort = () => events.push("onabort");
+ e.preventDefault();
+ });
+
+ await promise_rejects_dom(t, 'AbortError', navigation.navigate("?1").committed);
+ assert_true(abort_signal.aborted);
+ assert_array_equals(events, ["onabort", "onnavigateerror"]);
+}, "event.preventDefault() signals event.signal");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-window-stop-after-intercept.html b/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-window-stop-after-intercept.html
new file mode 100644
index 0000000000..51ba7753a8
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-window-stop-after-intercept.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ let start_url = location.href;
+ let abort_signal;
+ let onabort_called = false;
+ let navigateErrorException;
+ navigation.onnavigateerror = t.step_func(e => {
+ assert_equals(e.constructor, ErrorEvent);
+ navigateErrorException = e.error;
+ assert_equals(e.filename, start_url);
+ assert_greater_than(e.lineno, 0);
+ assert_greater_than(e.colno, 0);
+ });
+ navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess");
+ navigation.onnavigate = t.step_func(e => {
+ abort_signal = e.signal;
+ abort_signal.onabort = () => onabort_called = true;
+ e.intercept({ handler: () => new Promise(resolve => t.step_timeout(resolve, 0)) });
+ });
+ let result = navigation.navigate("?1");
+ window.stop();
+ assert_true(abort_signal.aborted);
+ assert_true(onabort_called);
+
+ result.committed.then(() => {
+ return promise_rejects_dom(t, 'AbortError', result.finished);
+ }).then(() => {
+ return result.finished.catch(e => assert_equals(e, navigateErrorException));
+ }).then(() => {
+ // Complete the test asynchronously to ensure that onnavigatesuccess
+ // didn't fire on a microtask.
+ t.step_timeout(t.step_func_done(() => {}), 5);
+ });
+ });
+}, "window.stop() cancels the navigate event's intercept() and signals event.signal");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-window-stop-in-onnavigate.html b/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-window-stop-in-onnavigate.html
new file mode 100644
index 0000000000..1b406c42d3
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-window-stop-in-onnavigate.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ window.onload = t.step_func_done(() => {
+ let abort_signal;
+ let onabort_called = false;
+ let canceled_in_second_handler = false;
+ navigation.addEventListener("navigate", t.step_func(e => {
+ abort_signal = e.signal;
+ abort_signal.onabort = () => onabort_called = true;
+ window.stop();
+ }));
+ navigation.addEventListener("navigate", t.step_func(e => {
+ canceled_in_second_handler = e.defaultPrevented;
+ }));
+ navigation.navigate("?1");
+ assert_true(abort_signal.aborted);
+ assert_true(onabort_called);
+ assert_true(canceled_in_second_handler);
+ });
+}, "window.stop() signals event.signal inside a navigate event handler");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-window-stop.html b/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-window-stop.html
new file mode 100644
index 0000000000..43e005e8b4
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigate-event/signal-abort-window-stop.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ let abort_signal;
+ let onabort_called = false;
+ navigation.onnavigatesuccess = t.unreached_func("onnavigatesuccess");
+ navigation.onnavigate = t.step_func(e => {
+ abort_signal = e.signal;
+ abort_signal.onabort = () => onabort_called = true;
+ });
+ navigation.navigate("?1");
+ window.stop();
+ assert_true(abort_signal.aborted);
+ assert_true(onabort_called);
+ // Complete the test asynchronously to ensure that onnavigatesuccess
+ // didn't fire on a microtask.
+ t.step_timeout(t.step_func_done(() => {}), 0);
+ });
+}, "window.stop() signals event.signal");
+</script>