summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/navigation-api/navigation-history-entry
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/navigation-api/navigation-history-entry
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/navigation-api/navigation-history-entry')
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/after-detach.html30
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/current-basic.html107
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entries-across-origins.html33
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-bfcache-in-iframe.html36
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-bfcache.html60
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-blank-navigation-from-cross-origin.html22
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-blank-navigation.html25
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-blob-navigation.html25
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-cross-document-forward-pruning.html33
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-javascript-url-navigation.html38
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-navigations-in-multiple-windows.html34
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-srcdoc-navigation.html25
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entries-array-equality.html8
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entries-in-new-javascript-url-iframe.html15
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entries-in-new-srcdoc-iframe.html15
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entries-when-inactive.html16
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/entry-after-detach.html20
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/index-not-in-entries.html24
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-back-cross-document.html15
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-back-same-document.html24
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-reload-intercept.html23
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-reload.html18
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-replace-cross-origin.html17
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-replace.html18
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/no-referrer-dynamic-url-censored.html33
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/no-referrer-from-meta-url-censored.html32
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/no-referrer-url-censored.html32
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/opaque-origin-data-url.html17
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/opaque-origin.html9
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/resources/is_uuid.js3
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/resources/key-navigate-back-cross-document-helper.html27
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/resources/no-referrer-meta.html2
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/resources/no-referrer.html1
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/resources/no-referrer.html.headers1
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/resources/opaque-origin-page.html25
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/resources/post-entries-length-to-top.html7
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/resources/post-key-to-top.html3
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/sameDocument-after-fragment-navigate.html30
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/sameDocument-after-navigate-restore.html25
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/sameDocument-after-navigate.html14
-rw-r--r--testing/web-platform/tests/navigation-api/navigation-history-entry/state-after-navigate-restore.html27
41 files changed, 969 insertions, 0 deletions
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/after-detach.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/after-detach.html
new file mode 100644
index 0000000000..c4ecfec44d
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/after-detach.html
@@ -0,0 +1,30 @@
+<!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 i_navigation = i.contentWindow.navigation;
+
+ await i_navigation.navigate("#1").finished;
+ await i_navigation.navigate("#2").finished;
+ await i_navigation.back().finished;
+
+ assert_not_equals(i_navigation, null);
+ assert_not_equals(i_navigation.currentEntry, null);
+ assert_equals(i_navigation.entries().length, 3);
+ assert_true(i_navigation.canGoBack, "canGoBack");
+ assert_true(i_navigation.canGoForward, "canGoForward");
+
+ i.remove();
+
+ assert_equals(i_navigation.currentEntry, null);
+ assert_equals(i_navigation.entries().length, 0);
+ assert_false(i_navigation.canGoBack);
+ assert_false(i_navigation.canGoForward);
+}, "navigation.currentEntry/entries()/canGoBack/canGoForward after iframe removal");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/current-basic.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/current-basic.html
new file mode 100644
index 0000000000..78bbbb0560
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/current-basic.html
@@ -0,0 +1,107 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/is_uuid.js"></script>
+<script>
+test(() => {
+ let first_entry = navigation.currentEntry;
+ assert_not_equals(first_entry, null);
+ assert_not_equals(first_entry.key, null);
+ assert_true(isUUID(first_entry.key));
+ assert_not_equals(first_entry.id, null);
+ assert_true(isUUID(first_entry.id));
+ assert_equals(first_entry.url, location.href);
+ assert_true(first_entry.sameDocument);
+ assert_equals(navigation.entries().length, 1);
+ assert_equals(first_entry, navigation.entries()[0]);
+
+ history.replaceState(2, "", "#2");
+ let second_entry = navigation.currentEntry;
+ assert_not_equals(second_entry, first_entry);
+ assert_equals(second_entry.key, first_entry.key);
+ assert_true(isUUID(second_entry.key));
+ assert_not_equals(second_entry.id, first_entry.id);
+ assert_true(isUUID(second_entry.id));
+ assert_equals(second_entry.url, location.href);
+ assert_true(second_entry.sameDocument);
+ assert_equals(navigation.entries().length, 1);
+ assert_equals(second_entry, navigation.entries()[0]);
+
+ history.pushState(3, "", "#3");
+ let third_entry = navigation.currentEntry;
+ assert_not_equals(third_entry, second_entry);
+ assert_not_equals(third_entry.key, second_entry.key);
+ assert_true(isUUID(third_entry.key));
+ assert_not_equals(third_entry.id, second_entry.id);
+ assert_true(isUUID(third_entry.id));
+ assert_equals(third_entry.url, location.href);
+ assert_true(third_entry.sameDocument);
+ assert_equals(navigation.entries().length, 2);
+ assert_equals(third_entry, navigation.entries()[1]);
+
+ history.pushState(4, "");
+ let fourth_entry = navigation.currentEntry;
+ assert_not_equals(fourth_entry, third_entry);
+ assert_not_equals(fourth_entry.key, third_entry.key);
+ assert_true(isUUID(fourth_entry.key));
+ assert_not_equals(fourth_entry.id, third_entry.id);
+ assert_true(isUUID(fourth_entry.id));
+ assert_equals(fourth_entry.url, third_entry.url);
+ assert_true(fourth_entry.sameDocument);
+ assert_equals(navigation.entries().length, 3);
+ assert_equals(fourth_entry, navigation.entries()[2]);
+
+ history.replaceState(5, "");
+ let fifth_entry = navigation.currentEntry;
+ assert_not_equals(fifth_entry, fourth_entry);
+ assert_equals(fifth_entry.key, fourth_entry.key);
+ assert_true(isUUID(fifth_entry.key));
+ assert_not_equals(fifth_entry.id, fourth_entry.id);
+ assert_true(isUUID(fifth_entry.id));
+ assert_equals(fifth_entry.url, fourth_entry.url);
+ assert_true(fifth_entry.sameDocument);
+ assert_equals(navigation.entries().length, 3);
+ assert_equals(fifth_entry, navigation.entries()[2]);
+
+ history.pushState(5, "");
+ let fifth_entry_after_push = navigation.currentEntry;
+ assert_not_equals(fifth_entry_after_push, fifth_entry);
+ assert_not_equals(fifth_entry_after_push.key, fifth_entry.key);
+ assert_true(isUUID(fifth_entry_after_push.key));
+ assert_not_equals(fifth_entry_after_push.id, fifth_entry.id);
+ assert_true(isUUID(fifth_entry_after_push.id));
+ assert_equals(fifth_entry_after_push.url, fifth_entry.url);
+ assert_true(fifth_entry_after_push.sameDocument);
+ assert_equals(navigation.entries().length, 4);
+ assert_equals(fifth_entry_after_push, navigation.entries()[3]);
+
+ history.replaceState(5, "");
+ let fifth_entry_after_replace = navigation.currentEntry;
+ assert_not_equals(fifth_entry_after_replace, fifth_entry_after_push);
+ assert_equals(fifth_entry_after_replace.key, fifth_entry_after_push.key);
+ assert_true(isUUID(fifth_entry_after_replace.key));
+ assert_not_equals(fifth_entry_after_replace.id, fifth_entry_after_push.id);
+ assert_true(isUUID(fifth_entry_after_replace.id));
+ assert_equals(fifth_entry_after_replace.url, fifth_entry_after_push.url);
+ assert_true(fifth_entry_after_replace.sameDocument);
+ assert_equals(navigation.entries().length, 4);
+ assert_equals(fifth_entry_after_replace, navigation.entries()[3]);
+
+ location.hash = "6";
+ let sixth_entry = navigation.currentEntry;
+ assert_not_equals(sixth_entry, fifth_entry_after_replace);
+ assert_equals(sixth_entry.key, fifth_entry_after_replace.key);
+ assert_true(isUUID(sixth_entry.key));
+ assert_not_equals(sixth_entry.id, fifth_entry_after_replace.id);
+ assert_true(isUUID(sixth_entry.id));
+ assert_not_equals(sixth_entry.url, fifth_entry_after_replace.url);
+ assert_true(sixth_entry.sameDocument);
+ assert_equals(navigation.entries().length, 4);
+ assert_equals(sixth_entry, navigation.entries()[3]);
+
+ navigation.entries().forEach(entry => {
+ assert_true(isUUID(entry.id));
+ assert_true(isUUID(entry.key));
+ });
+}, "Basic tests for navigation.currentEntry");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-across-origins.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-across-origins.html
new file mode 100644
index 0000000000..447273bff2
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-across-origins.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>
+<script src="resources/is_uuid.js"></script>
+<iframe id="i" src="/common/blank.html"></iframe>
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ let start_key = i.contentWindow.navigation.currentEntry.key;
+ let start_id = i.contentWindow.navigation.currentEntry.id;
+
+ let cross_origin_url = new URL("resources/post-entries-length-to-top.html", location.href);
+ cross_origin_url.hostname = get_host_info().REMOTE_HOST;
+ i.contentWindow.location.assign(cross_origin_url.href);
+
+ window.onmessage = t.step_func(e => {
+ assert_equals(e.data, 1);
+
+ i.src = "/common/blank.html?2";
+ i.onload = t.step_func_done(() => {
+ let entries = i.contentWindow.navigation.entries();
+ assert_equals(entries.length, 1);
+ assert_equals(new URL(entries[0].url).search, "?2");
+ assert_not_equals(entries[0].key, start_key);
+ assert_not_equals(entries[0].id, start_id);
+ assert_true(isUUID(entries[0].key));
+ assert_true(isUUID(entries[0].id));
+ });
+ });
+ });
+}, "navigation.entries() should only contain entries that are both same-origin and contiguous");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-bfcache-in-iframe.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-bfcache-in-iframe.html
new file mode 100644
index 0000000000..b54a749950
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-bfcache-in-iframe.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js"></script>
+<script src="resources/is_uuid.js"></script>
+
+<script>
+// This test ensures that navigation.entries() in an iframe is properly updated
+// when a page is restored from bfcache.
+// First, create an iframe and do a fragment navigation in it, so that its
+// navigation.entries().length == 2. Then go back, so that entries()[0] is
+// current. Finally, navigate the main window (which should clobber the
+// the iframe's entries()[1]), and come back via bfcache. If the iframe's
+// entries() were updated, then its entries().length should have been reduced
+// to 1.
+runBfcacheTest({
+ targetOrigin: originSameOrigin,
+ funcBeforeNavigation: async () => {
+ window.events = [];
+ let i = document.createElement("iframe");
+ i.src = "/common/blank.html";
+ document.body.appendChild(i);
+ await new Promise(resolve => i.onload = () => setTimeout(resolve, 0));
+ await i.contentWindow.navigation.navigate("#foo");
+ await i.contentWindow.navigation.back();
+ window.frames[0].navigation.entries()[1].ondispose = () => events.push("dispose");
+ window.frames[0].onpageshow = () => events.push("pageshow");
+ },
+ async funcAfterAssertion(pageA, pageB) {
+ assert_equals(await pageA.execute_script(() => window.frames[0].navigation.entries().length), 1);
+ assert_array_equals(await pageA.execute_script(() => window.events), ["pageshow", "dispose"]);
+ }
+}, "entries() in an iframe must be updated after navigating back to a bfcached page");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-bfcache.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-bfcache.html
new file mode 100644
index 0000000000..ef93d1e27e
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-bfcache.html
@@ -0,0 +1,60 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js"></script>
+<script src="resources/is_uuid.js"></script>
+
+<script>
+// This test ensures that navigation.entries() is properly updated when a page
+// is restored from bfcache. Before navigating away and back, entries() contains
+// a single entry representing this document. When restored from bfcache,
+// entries() should now have two entries: [0] should still be this document, but
+// [1] should represent the document that we navigated to and back from
+// (assuming that document is same-origin to this one).
+runBfcacheTest({
+ targetOrigin: originSameOrigin,
+ funcBeforeNavigation: () => {
+ window.originalEntry0 = navigation.entries()[0];
+ },
+ async funcAfterAssertion(pageA, pageB) {
+ const entryData = await pageA.execute_script(() => {
+ return navigation.entries().map(e => ({
+ url: e.url,
+ key: e.key,
+ id: e.id,
+ index: e.index,
+ sameDocument: e.sameDocument
+ }));
+ });
+
+ assert_equals(entryData.length, 2);
+
+ // Ensure that [1] has the proper url, and otherwise is initialized as
+ // a cross-document NavigationHistoryEntry ought to be.
+ assert_equals(entryData[0].url, pageA.url);
+ assert_equals(entryData[1].url, pageB.url);
+
+ assert_true(isUUID(entryData[0].key));
+ assert_true(isUUID(entryData[1].key));
+ assert_not_equals(entryData[0].key, entryData[1].key);
+
+ assert_true(isUUID(entryData[0].id));
+ assert_true(isUUID(entryData[1].id));
+ assert_not_equals(entryData[0].id, entryData[1].id);
+
+ assert_equals(entryData[0].index, 0);
+ assert_equals(entryData[1].index, 1);
+
+ assert_true(entryData[0].sameDocument);
+ assert_false(entryData[1].sameDocument);
+
+ const currentIsZero = await pageA.execute_script(() => navigation.currentEntry === navigation.entries()[0]);
+ assert_true(currentIsZero);
+
+ const zeroIsSameAsOriginal = await pageA.execute_script(() => navigation.currentEntry === window.originalEntry0);
+ assert_true(zeroIsSameAsOriginal);
+ }
+}, "entries() must contain the forward-history page after navigating back to a bfcached page");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-blank-navigation-from-cross-origin.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-blank-navigation-from-cross-origin.html
new file mode 100644
index 0000000000..d527637ed3
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-blank-navigation-from-cross-origin.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+<script>
+promise_test(async t => {
+ await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));
+
+ let i = document.createElement("iframe");
+ i.src = get_host_info().HTTP_ORIGIN_WITH_DIFFERENT_PORT + "/common/blank.html";
+ document.body.appendChild(i);
+ await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));
+
+ i.contentWindow.location = "about:blank";
+ await new Promise(resolve => i.onload = resolve);
+ let entries = i.contentWindow.navigation.entries();
+ assert_equals(entries.length, 1);
+ assert_equals(entries[0].url, "about:blank");
+}, "entries() should not be leaked from cross-origin when navigating to about:blank");
+</script>
+</body>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-blank-navigation.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-blank-navigation.html
new file mode 100644
index 0000000000..f54ae06e14
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-blank-navigation.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/is_uuid.js"></script>
+<iframe id="i" src="/common/blank.html"></iframe>
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ i.onload = t.step_func_done(() => {
+ let entries = i.contentWindow.navigation.entries();
+ assert_equals(entries.length, 2);
+ assert_not_equals(entries[1].key, entries[0].key);
+ assert_not_equals(entries[1].url, entries[0].url);
+ assert_equals(entries[1].url, "about:blank");
+ assert_not_equals(entries[1].id, entries[0].id);
+
+ assert_true(isUUID(entries[0].key));
+ assert_true(isUUID(entries[0].id));
+ assert_true(isUUID(entries[1].key));
+ assert_true(isUUID(entries[1].id));
+ });
+ i.src = "about:blank";
+ });
+}, "entries() after navigation to about:blank");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-blob-navigation.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-blob-navigation.html
new file mode 100644
index 0000000000..67611f4d44
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-blob-navigation.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/is_uuid.js"></script>
+<iframe id="i" src="/common/blank.html"></iframe>
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ i.onload = t.step_func_done(() => {
+ let entries = i.contentWindow.navigation.entries();
+ assert_equals(entries.length, 2);
+ assert_not_equals(entries[1].key, entries[0].key);
+ assert_not_equals(entries[1].url, entries[0].url);
+ assert_equals(new URL(entries[1].url).protocol, "blob:");
+ assert_not_equals(entries[1].id, entries[0].id);
+
+ assert_true(isUUID(entries[0].key));
+ assert_true(isUUID(entries[0].id));
+ assert_true(isUUID(entries[1].key));
+ assert_true(isUUID(entries[1].id));
+ });
+ i.src = URL.createObjectURL(new Blob(["<body></body>"]));
+ });
+}, "entries() after navigation to a blob: URL");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-cross-document-forward-pruning.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-cross-document-forward-pruning.html
new file mode 100644
index 0000000000..ddd1ad571d
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-cross-document-forward-pruning.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/is_uuid.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(r => window.onload = () => t.step_timeout(r, 0));
+
+ i.contentWindow.location.search = "?2";
+ await new Promise(r => i.onload = () => t.step_timeout(r, 0));
+
+ i.contentWindow.location.search = "?3";
+ await new Promise(r => i.onload = () => t.step_timeout(r, 0));
+
+ i.contentWindow.history.back();
+ await new Promise(r => i.onload = () => t.step_timeout(r, 0));
+
+ i.contentWindow.history.back();
+ await new Promise(r => i.onload = () => t.step_timeout(r, 0));
+
+ i.contentWindow.location.search = "?fork";
+ await new Promise(r => i.onload = () => t.step_timeout(r, 0));
+
+ const entries = i.contentWindow.navigation.entries();
+ const searches = entries.map(e => (new URL(e.url)).search);
+ assert_array_equals(searches, ["", "?fork"]);
+
+ assert_equals(i.contentWindow.navigation.entries().at(-1), i.contentWindow.navigation.currentEntry);
+}, "navigation.entries() behavior after forward-pruning due to cross-document navs");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-javascript-url-navigation.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-javascript-url-navigation.html
new file mode 100644
index 0000000000..c5ef7f33e4
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-javascript-url-navigation.html
@@ -0,0 +1,38 @@
+<!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 start_key = i.contentWindow.navigation.currentEntry.key;
+ let start_url = i.contentWindow.navigation.currentEntry.url;
+ let start_id = i.contentWindow.navigation.currentEntry.id;
+ let did_js_url_nav = false;
+ i.onload = t.step_func(() => {
+ if (!did_js_url_nav) {
+ assert_equals(i.contentWindow.navigation.entries().length, 2);
+ before_js_nav_key = i.contentWindow.navigation.currentEntry.key;
+ before_js_nav_url = i.contentWindow.navigation.currentEntry.url;
+ before_js_nav_id = i.contentWindow.navigation.currentEntry.id;
+ i.src = "javascript:'new content'";
+ did_js_url_nav = true;
+ } else {
+ assert_equals(i.contentWindow.navigation.entries().length, 2);
+ let first_entry = i.contentWindow.navigation.entries()[0];
+ let js_url_entry = i.contentWindow.navigation.entries()[1];
+ assert_equals(first_entry.key, start_key);
+ assert_equals(first_entry.url, start_url);
+ assert_equals(first_entry.id, start_id);
+
+ assert_equals(js_url_entry, i.contentWindow.navigation.currentEntry);
+ assert_equals(js_url_entry.key, before_js_nav_key);
+ assert_equals(js_url_entry.url, before_js_nav_url);
+ assert_not_equals(js_url_entry.id, before_js_nav_id);
+ t.done();
+ }
+ });
+ i.contentWindow.navigation.navigate("?1");
+ });
+}, "entries() after navigation to a javascript: URL");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-navigations-in-multiple-windows.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-navigations-in-multiple-windows.html
new file mode 100644
index 0000000000..d1d4d86eb3
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-navigations-in-multiple-windows.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/is_uuid.js"></script>
+<iframe id="i" src="/common/blank.html"></iframe>
+<script>
+async_test(t => {
+ // The navigations in each window should have only added an navigation to
+ // their own window.
+ function assertExpectedEntries(entries, expected_url) {
+ assert_equals(entries.length, 2);
+ assert_not_equals(entries[1].key, entries[0].key);
+ assert_not_equals(entries[1].url, entries[0].url);
+ assert_not_equals(entries[1].id, entries[0].id);
+ assert_true(isUUID(entries[0].key));
+ assert_true(isUUID(entries[0].id));
+ assert_true(isUUID(entries[1].key));
+ assert_true(isUUID(entries[1].id));
+
+ assert_equals(entries[1].url, expected_url);
+ }
+
+ // 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(() => {
+ location.hash = "#1";
+ i.onload = t.step_func_done(() => {
+ assertExpectedEntries(navigation.entries(), location.href);
+ assertExpectedEntries(i.contentWindow.navigation.entries(), i.contentWindow.location.href);
+ });
+ i.contentWindow.location = "/common/blank.html?2";
+ }), 0);
+}, "navigation.entries() behavior when multiple windows navigate.");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-srcdoc-navigation.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-srcdoc-navigation.html
new file mode 100644
index 0000000000..b80e8aa0e2
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-after-srcdoc-navigation.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/is_uuid.js"></script>
+<iframe id="i" src="/common/blank.html"></iframe>
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ i.onload = t.step_func_done(() => {
+ let entries = i.contentWindow.navigation.entries();
+ assert_equals(entries.length, 2);
+ assert_not_equals(entries[1].key, entries[0].key);
+ assert_not_equals(entries[1].url, entries[0].url);
+ assert_equals(entries[1].url, "about:srcdoc");
+ assert_not_equals(entries[1].id, entries[0].id);
+
+ assert_true(isUUID(entries[0].key));
+ assert_true(isUUID(entries[0].id));
+ assert_true(isUUID(entries[1].key));
+ assert_true(isUUID(entries[1].id));
+ });
+ i.srcdoc = "new";
+ });
+}, "entries() after setting a srcdoc attribute");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-array-equality.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-array-equality.html
new file mode 100644
index 0000000000..98efb6b20c
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-array-equality.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+ assert_not_equals(navigation.entries(), navigation.entries());
+}, "navigation.entries() should not return an identical object on repeated invocations");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-in-new-javascript-url-iframe.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-in-new-javascript-url-iframe.html
new file mode 100644
index 0000000000..6f217f5e3c
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-in-new-javascript-url-iframe.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="javascript:'foo'"></iframe>
+
+<script>
+promise_test(async t => {
+ await new Promise(resolve => window.onload = resolve);
+
+ assert_not_equals(i.contentWindow.navigation.currentEntry, null);
+ assert_array_equals(i.contentWindow.navigation.entries(), [i.contentWindow.navigation.currentEntry]);
+
+ assert_equals(i.contentWindow.navigation.currentEntry.url, "about:blank");
+}, "entries() and currentEntry should be set in a new javascript: URL iframe");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-in-new-srcdoc-iframe.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-in-new-srcdoc-iframe.html
new file mode 100644
index 0000000000..a7e0f88d37
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-in-new-srcdoc-iframe.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" srcdoc="new"></iframe>
+
+<script>
+promise_test(async t => {
+ await new Promise(resolve => window.onload = resolve);
+
+ assert_not_equals(i.contentWindow.navigation.currentEntry, null);
+ assert_array_equals(i.contentWindow.navigation.entries(), [i.contentWindow.navigation.currentEntry]);
+
+ assert_equals(i.contentWindow.navigation.currentEntry.url, "about:srcdoc");
+}, "entries() and currentEntry should be set in a new srcdoc iframe");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-when-inactive.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-when-inactive.html
new file mode 100644
index 0000000000..c70b6d8bf8
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entries-when-inactive.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ const first_entry = navigation.entries()[0];
+ history.pushState(1, "", "#1");
+ assert_equals(navigation.entries()[0], first_entry);
+ history.back();
+ window.onpopstate = t.step_func_done(() => {
+ const second_entry = navigation.entries()[1];
+ history.replaceState(0, "", "#0");
+ assert_equals(navigation.entries()[1], second_entry);
+ });
+}, "A non-active entry in navigation.entries() should not be modified when a different entry is modified");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/entry-after-detach.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/entry-after-detach.html
new file mode 100644
index 0000000000..69c52d1409
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/entry-after-detach.html
@@ -0,0 +1,20 @@
+<!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_done(() => {
+ let i_entry = i.contentWindow.navigation.currentEntry;
+ assert_true(i_entry.sameDocument);
+ assert_not_equals(i_entry.url, null);
+ assert_not_equals(i_entry.key, "");
+ assert_not_equals(i_entry.id, "");
+ i.remove();
+ assert_false(i_entry.sameDocument);
+ assert_equals(i_entry.url, null);
+ assert_equals(i_entry.key, "");
+ assert_equals(i_entry.id, "");
+ });
+}, "NavigationHistoryEntry properties after detach");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/index-not-in-entries.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/index-not-in-entries.html
new file mode 100644
index 0000000000..a16d130ba1
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/index-not-in-entries.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="/common/blank.html"></iframe>
+<script>
+async_test(t => {
+ // Wait for after the load event so that the navigation doesn't get converted
+ // into a replace navigation.
+ window.onload = () => t.step_timeout(t.step_func_done(() => {
+ // Remove the entry by replacing it.
+ let replaced_entry = navigation.currentEntry;
+ assert_equals(replaced_entry.index, 0);
+ navigation.navigate("#0", { history: "replace" });
+ assert_equals(replaced_entry.index, -1);
+
+ // Remove the entry by detaching its window.
+ let iframe_entry = i.contentWindow.navigation.currentEntry;
+ assert_equals(iframe_entry.index, 0);
+ i.remove();
+ assert_equals(iframe_entry.index, -1);
+ t.done();
+ }), 0);
+}, "entry.index should return -1 when not in navigation.entries()");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-back-cross-document.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-back-cross-document.html
new file mode 100644
index 0000000000..2dd58c03e9
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-back-cross-document.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="resources/key-navigate-back-cross-document-helper.html"></iframe>
+<script>
+async_test(t => {
+ window.finish = t.step_func_done((end_key, end_id) => {
+ assert_equals(window.start_key, end_key);
+ assert_equals(window.start_id, end_id);
+ // The new history entry in the iframe should not add any entries to
+ // this window's navigation.
+ assert_equals(navigation.entries().length, 1);
+ });
+}, "NavigationHistoryEntry's key and id on cross-document back navigation");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-back-same-document.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-back-same-document.html
new file mode 100644
index 0000000000..858b5fd2c8
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-back-same-document.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ let key = navigation.currentEntry.key;
+ let id = navigation.currentEntry.id;
+ assert_equals(navigation.entries().length, 1);
+
+ history.pushState("hash", "", "#hash");
+ assert_not_equals(key, navigation.currentEntry.key);
+ assert_not_equals(id, navigation.currentEntry.id);
+ assert_equals(navigation.entries().length, 2);
+ assert_equals(navigation.currentEntry.index, 1);
+
+ window.onpopstate = t.step_func_done(() => {
+ assert_equals(key, navigation.currentEntry.key);
+ assert_equals(id, navigation.currentEntry.id);
+ assert_equals(navigation.entries().length, 2);
+ assert_equals(navigation.currentEntry.index, 0);
+ });
+ history.back();
+}, "NavigationHistoryEntry's key and id on same-document back navigation");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-reload-intercept.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-reload-intercept.html
new file mode 100644
index 0000000000..62ce097439
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-reload-intercept.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 => {
+ await new Promise(resolve => { window.onload = resolve; });
+
+
+ const original = i.contentWindow.navigation.currentEntry;
+ const { key, id } = original;
+
+ i.contentWindow.navigation.addEventListener("navigate", e => e.intercept());
+
+ i.onload = t.unreached_func("the iframe must not reload");
+
+ await i.contentWindow.location.reload();
+
+ assert_equals(i.contentWindow.navigation.currentEntry, original);
+ assert_equals(i.contentWindow.navigation.currentEntry.key, key);
+ assert_equals(i.contentWindow.navigation.currentEntry.id, id);
+}, "NavigationHistoryEntry's key and id after location.reload() intercepted by intercept()");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-reload.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-reload.html
new file mode 100644
index 0000000000..f950e2f3a2
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-reload.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 key = i.contentWindow.navigation.currentEntry.key;
+ let id = i.contentWindow.navigation.currentEntry.id;
+ i.contentWindow.location.reload();
+ i.onload = t.step_func_done(() => {
+ assert_equals(key, i.contentWindow.navigation.currentEntry.key);
+ assert_equals(id, i.contentWindow.navigation.currentEntry.id);
+ assert_equals(navigation.entries().length, 1);
+ });
+ });
+}, "NavigationHistoryEntry's key and id after location.reload()");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-replace-cross-origin.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-replace-cross-origin.html
new file mode 100644
index 0000000000..65aff4a3ab
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-replace-cross-origin.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<iframe id="i" src="/common/blank.html"></iframe>
+<script>
+async_test(t => {
+ window.onload = t.step_func(() => {
+ let key_before_replace = i.contentWindow.navigation.currentEntry.key;
+ window.onmessage = t.step_func_done(e => assert_not_equals(key_before_replace, e.data));
+
+ let cross_origin_url = new URL("resources/post-key-to-top.html", location.href);
+ cross_origin_url.hostname = get_host_info().REMOTE_HOST;
+ i.contentWindow.location.replace(cross_origin_url.href);
+ });
+}, "NavigationHistoryEntry's key and id after location.replace()");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-replace.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-replace.html
new file mode 100644
index 0000000000..a58772a7f4
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/key-id-location-replace.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 key = i.contentWindow.navigation.currentEntry.key;
+ let id = i.contentWindow.navigation.currentEntry.id;
+ i.contentWindow.location.replace("/common/blank.html?query");
+ i.onload = t.step_func_done(() => {
+ assert_equals(key, i.contentWindow.navigation.currentEntry.key);
+ assert_not_equals(id, i.contentWindow.navigation.currentEntry.id);
+ assert_equals(navigation.entries().length, 1);
+ });
+ });
+}, "NavigationHistoryEntry's key and id after location.replace()");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/no-referrer-dynamic-url-censored.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/no-referrer-dynamic-url-censored.html
new file mode 100644
index 0000000000..7a5544c419
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/no-referrer-dynamic-url-censored.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(r => window.onload = () => t.step_timeout(r, 0));
+
+ // The entry for the first document has a visible url.
+ i.contentWindow.navigation.navigate("/common/blank.html?2");
+ await new Promise(r => i.onload = () => t.step_timeout(r, 0));
+ assert_not_equals(i.contentWindow.navigation.entries()[0].url, null);
+
+ // Apply no-referrer, the url should now be censored when no longer on that document.
+ i.contentWindow.navigation.back();
+ await new Promise(r => i.onload = () => t.step_timeout(r, 0));
+ i.contentDocument.head.innerHTML = `<meta name="referrer" content="no-referrer">`;
+ assert_not_equals(i.contentWindow.navigation.entries()[0].url, null);
+ i.contentWindow.navigation.forward();
+ await new Promise(r => i.onload = () => t.step_timeout(r, 0));
+ assert_equals(i.contentWindow.navigation.entries()[0].url, null);
+
+ // Overwrite the referrer policy, the url should be visible again.
+ i.contentWindow.navigation.back();
+ await new Promise(r => i.onload = () => t.step_timeout(r, 0));
+ i.contentDocument.head.innerHTML = `<meta name="referrer" content="same-origin">`;
+ i.contentWindow.navigation.forward();
+ await new Promise(r => i.onload = () => t.step_timeout(r, 0));
+ assert_not_equals(i.contentWindow.navigation.entries()[0].url, null);
+}, "The url of a document is censored by a no-referrer policy dynamically");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/no-referrer-from-meta-url-censored.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/no-referrer-from-meta-url-censored.html
new file mode 100644
index 0000000000..fc563f509e
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/no-referrer-from-meta-url-censored.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="resources/no-referrer-meta.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(r => window.onload = () => t.step_timeout(r, 0));
+
+ await i.contentWindow.navigation.navigate("#hash");
+ assert_equals(i.contentWindow.navigation.entries().length, 2);
+
+ // The entries for no-referrer.html should have the url censored.
+ i.contentWindow.navigation.navigate("/common/blank.html");
+ await new Promise(r => i.onload = () => t.step_timeout(r, 0));
+ assert_equals(i.contentWindow.navigation.entries().length, 3);
+ assert_equals(i.contentWindow.navigation.currentEntry.index, 2);
+ assert_equals(i.contentWindow.navigation.entries()[0].url, null);
+ assert_equals(i.contentWindow.navigation.entries()[1].url, null);
+
+ // Navigating back to no-referrer.html should uncensor the urls.
+ i.contentWindow.navigation.back();
+ await new Promise(r => i.onload = () => t.step_timeout(r, 0));
+ assert_equals(i.contentWindow.navigation.entries().length, 3);
+ assert_equals(i.contentWindow.navigation.currentEntry.index, 1);
+ assert_equals(new URL(i.contentWindow.navigation.entries()[0].url).pathname,
+ "/navigation-api/navigation-history-entry/resources/no-referrer-meta.html");
+ assert_equals(new URL(i.contentWindow.navigation.entries()[1].url).pathname,
+ "/navigation-api/navigation-history-entry/resources/no-referrer-meta.html");
+}, "The url of a document with no-referrer referrer meta tag is censored in NavigationHistoryEntry");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/no-referrer-url-censored.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/no-referrer-url-censored.html
new file mode 100644
index 0000000000..e7eb1afc7d
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/no-referrer-url-censored.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="resources/no-referrer.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(r => window.onload = () => t.step_timeout(r, 0));
+
+ await i.contentWindow.navigation.navigate("#hash");
+ assert_equals(i.contentWindow.navigation.entries().length, 2);
+
+ // The entries for no-referrer.html should have the url censored.
+ i.contentWindow.navigation.navigate("/common/blank.html");
+ await new Promise(r => i.onload = () => t.step_timeout(r, 0));
+ assert_equals(i.contentWindow.navigation.entries().length, 3);
+ assert_equals(i.contentWindow.navigation.currentEntry.index, 2);
+ assert_equals(i.contentWindow.navigation.entries()[0].url, null);
+ assert_equals(i.contentWindow.navigation.entries()[1].url, null);
+
+ // Navigating back to no-referrer.html should uncensor the urls.
+ i.contentWindow.navigation.back();
+ await new Promise(r => i.onload = () => t.step_timeout(r, 0));
+ assert_equals(i.contentWindow.navigation.entries().length, 3);
+ assert_equals(i.contentWindow.navigation.currentEntry.index, 1);
+ assert_equals(new URL(i.contentWindow.navigation.entries()[0].url).pathname,
+ "/navigation-api/navigation-history-entry/resources/no-referrer.html");
+ assert_equals(new URL(i.contentWindow.navigation.entries()[1].url).pathname,
+ "/navigation-api/navigation-history-entry/resources/no-referrer.html");
+}, "The url of a document with no-referrer referrer policy is censored in NavigationHistoryEntry");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/opaque-origin-data-url.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/opaque-origin-data-url.html
new file mode 100644
index 0000000000..65123fa60b
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/opaque-origin-data-url.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(() => {
+ window.onmessage = t.step_func_done(e => {
+ assert_equals(e.data.length, 0);
+ assert_true(e.data.currentIsNull);
+ });
+ i.src = "data:text/html,<script>top.postMessage({ length: navigation.entries().length, " +
+ "currentIsNull: navigation.currentEntry === null}, '*')</sc" +
+ "ript>";
+ });
+}, "entries() and currentEntry after navigation to a data: URL (which has an opaque origin)");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/opaque-origin.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/opaque-origin.html
new file mode 100644
index 0000000000..898ca27e4f
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/opaque-origin.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id="i" sandbox="allow-scripts" src="resources/opaque-origin-page.html"></iframe>
+
+<script>
+fetch_tests_from_window(i.contentWindow);
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/is_uuid.js b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/is_uuid.js
new file mode 100644
index 0000000000..3b855c01b0
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/is_uuid.js
@@ -0,0 +1,3 @@
+function isUUID(key) {
+ return /[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}/.test(key);
+}
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/key-navigate-back-cross-document-helper.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/key-navigate-back-cross-document-helper.html
new file mode 100644
index 0000000000..79f3c3da0a
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/key-navigate-back-cross-document-helper.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script>
+ // Wait for after the load event so that the navigation doesn't get converted
+ // into a replace navigation.
+window.onload = () => step_timeout(() => {
+ if (location.search == "?go-back") {
+ assert_equals(navigation.entries().length, 2);
+ assert_equals(navigation.currentEntry.index, 1);
+ // Step 2: Navigate back.
+ history.back();
+ return;
+ }
+ if (top.start_key) {
+ assert_equals(navigation.entries().length, 2);
+ assert_equals(navigation.currentEntry.index, 0);
+ // Step 3: Notify parent, which will ensure the same key is used after back navigation.
+ top.finish(navigation.currentEntry.key, navigation.currentEntry.id);
+ return;
+ }
+ // Step 1: Record initial key and navigate.
+ assert_equals(navigation.entries().length, 1);
+ top.start_key = navigation.currentEntry.key;
+ top.start_id = navigation.currentEntry.id;
+ location.search = "go-back";
+}, 0);
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/no-referrer-meta.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/no-referrer-meta.html
new file mode 100644
index 0000000000..bd5ec391cc
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/no-referrer-meta.html
@@ -0,0 +1,2 @@
+<meta name="referrer" content="no-referrer">
+<body></body>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/no-referrer.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/no-referrer.html
new file mode 100644
index 0000000000..c8b7661f42
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/no-referrer.html
@@ -0,0 +1 @@
+<body></body>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/no-referrer.html.headers b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/no-referrer.html.headers
new file mode 100644
index 0000000000..7ffbf17d6b
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/no-referrer.html.headers
@@ -0,0 +1 @@
+Referrer-Policy: no-referrer
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/opaque-origin-page.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/opaque-origin-page.html
new file mode 100644
index 0000000000..98e2c1b317
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/opaque-origin-page.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<!-- Put this page in a sandbox to give it an opaque origin -->
+
+<script>
+promise_test(async t => {
+ // Wait for after the load event so that the navigation doesn't get converted
+ // into a replace navigation.
+ await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));
+
+ location.hash = "#1";
+ await new Promise(resolve => window.onhashchange = resolve);
+ location.hash = "#2";
+ await new Promise(resolve => window.onhashchange = resolve);
+ history.back();
+ await new Promise(resolve => window.onhashchange = resolve);
+
+ assert_equals(location.hash, "#1");
+
+ assert_equals(navigation.currentEntry, null);
+ assert_equals(navigation.entries().length, 0);
+ assert_false(navigation.canGoBack);
+ assert_false(navigation.canGoForward);
+}, "navigation.currentEntry/entries()/canGoBack/canGoForward in an opaque origin iframe");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/post-entries-length-to-top.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/post-entries-length-to-top.html
new file mode 100644
index 0000000000..c8fe005d8e
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/post-entries-length-to-top.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script>
+// Wait for after the load event so that the navigation doesn't get converted
+// into a replace navigation.
+window.onload = () => step_timeout(() => top.postMessage(navigation.entries().length, "*"), 0);
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/post-key-to-top.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/post-key-to-top.html
new file mode 100644
index 0000000000..285f345dc1
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/resources/post-key-to-top.html
@@ -0,0 +1,3 @@
+<script>
+top.postMessage(navigation.currentEntry.key, "*");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/sameDocument-after-fragment-navigate.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/sameDocument-after-fragment-navigate.html
new file mode 100644
index 0000000000..a197f825d8
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/sameDocument-after-fragment-navigate.html
@@ -0,0 +1,30 @@
+<!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 entry1 = navigation.currentEntry;
+ assert_true(entry1.sameDocument);
+
+ location = "#hash";
+ let entry2 = navigation.currentEntry;
+ assert_not_equals(entry1, entry2);
+ assert_true(entry1.sameDocument);
+
+ history.pushState("push", "", "#push");
+ let entry3 = navigation.currentEntry;
+ assert_not_equals(entry1, entry3);
+ assert_not_equals(entry2, entry3);
+ assert_true(entry1.sameDocument);
+ assert_true(entry2.sameDocument);
+
+ assert_equals(navigation.entries().length, 3);
+ assert_equals(navigation.entries()[0], entry1);
+ assert_equals(navigation.entries()[1], entry2);
+ assert_equals(navigation.entries()[2], entry3);
+ }), 0);
+}, "entry.sameDocument after same-document navigations");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/sameDocument-after-navigate-restore.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/sameDocument-after-navigate-restore.html
new file mode 100644
index 0000000000..fd21bc222f
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/sameDocument-after-navigate-restore.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="/common/blank.html"></iframe>
+<script>
+promise_test(async t => {
+ await new Promise(resolve => window.onload = resolve);
+ await i.contentWindow.navigation.navigate("#foo");
+ assert_equals(i.contentWindow.navigation.entries().length, 2);
+ assert_true(i.contentWindow.navigation.entries()[0].sameDocument);
+
+ i.contentWindow.navigation.navigate("/common/blank.html?bar");
+ await new Promise(resolve => i.onload = resolve);
+ assert_equals(i.contentWindow.navigation.entries().length, 3);
+ assert_false(i.contentWindow.navigation.entries()[0].sameDocument);
+ assert_false(i.contentWindow.navigation.entries()[1].sameDocument);
+
+ i.contentWindow.navigation.back();
+ await new Promise(resolve => i.onload = resolve);
+ assert_equals(i.contentWindow.navigation.entries().length, 3);
+ assert_equals(i.contentWindow.navigation.currentEntry.index, 1);
+ assert_true(i.contentWindow.navigation.entries()[0].sameDocument);
+ assert_false(i.contentWindow.navigation.entries()[2].sameDocument);
+}, "entry.sameDocument is properly restored after cross-document back");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/sameDocument-after-navigate.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/sameDocument-after-navigate.html
new file mode 100644
index 0000000000..bfcb7c6599
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/sameDocument-after-navigate.html
@@ -0,0 +1,14 @@
+<!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 i_entry = i.contentWindow.navigation.currentEntry;
+ assert_true(i_entry.sameDocument);
+ i.onload = t.step_func_done(() => assert_false(i_entry.sameDocument));
+ i.contentWindow.location = "about:blank";
+ });
+}, "entry.sameDocument after cross-document navigation");
+</script>
diff --git a/testing/web-platform/tests/navigation-api/navigation-history-entry/state-after-navigate-restore.html b/testing/web-platform/tests/navigation-api/navigation-history-entry/state-after-navigate-restore.html
new file mode 100644
index 0000000000..097b1d5079
--- /dev/null
+++ b/testing/web-platform/tests/navigation-api/navigation-history-entry/state-after-navigate-restore.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>
+promise_test(async t => {
+ await new Promise(resolve => window.onload = resolve);
+ await i.contentWindow.navigation.navigate("#start", { history: "replace", state: "someState" });
+ assert_equals(i.contentWindow.navigation.entries().length, 1);
+ assert_equals(i.contentWindow.navigation.entries()[0].getState(), "someState");
+
+ await i.contentWindow.navigation.navigate("#foo");
+ assert_equals(i.contentWindow.navigation.entries().length, 2);
+ assert_equals(i.contentWindow.navigation.entries()[0].getState(), "someState");
+
+ i.contentWindow.navigation.navigate("/common/blank.html?bar");
+ await new Promise(resolve => i.onload = resolve);
+ assert_equals(i.contentWindow.navigation.entries().length, 3);
+ assert_equals(i.contentWindow.navigation.entries()[0].getState(), "someState");
+
+ i.contentWindow.navigation.back();
+ await new Promise(resolve => i.onload = resolve);
+ assert_equals(i.contentWindow.navigation.entries().length, 3);
+ assert_equals(i.contentWindow.navigation.currentEntry.index, 1);
+ assert_equals(i.contentWindow.navigation.entries()[0].getState(), "someState");
+}, "entry.getState() is properly restored after cross-document back");
+</script>