summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal')
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-1.html11
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-2.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001.html35
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/PopStateEvent.html47
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/api-availability.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-0.html35
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-1.html6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-2.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-3.html6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-4.html6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name.html13
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin-0.html34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin.html13
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_2.html43
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_3.html44
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/document-state.https.html133
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash-twice.html38
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash.html32
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-pushState.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-replaceState.html30
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash-twice.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash.html27
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-pushState.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-replaceState.html26
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/pushState-inside-popstate.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-immediate.html38
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-wait.html39
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/events.html151
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/hashchange_event.html49
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigate-parent-while-child-loading.html30
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigates-multiple-frames.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-bfcache-restore.html70
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-new-document-navigation.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-prerender-activation.html40
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/resources/order-in-prerender-activation-popup.html74
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank1.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank2.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/page-with-fragment.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/post_name_on_load.html7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resume-timer-on-history-back.html146
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic.html34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html71
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-samedoc.html55
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin.html71
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html81
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/popstate_event.html48
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/a.html1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-1.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-2.html3
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/b.html1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/c.html1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name-1.sub.html45
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name_stash.py13
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/same-url.html50
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/scroll-restoration-order.html74
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/consecutive-srcdoc.html85
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/srcdoc-history-entries.html95
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-cross-origin-main-frame-navigation-popup.sub.html10
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-same-origin-main-frame-navigation-1.sub.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-navigation.sub.html11
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-test.sub.html23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/unset_context_name.html32
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-aux-frame-navigation.sub.html17
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-main-frame-navigation.sub.html24
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-sub-frame-navigation.sub.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-aux-frame-navigation.sub.html17
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-main-frame-navigation.html12
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-sub-frame-navigation.sub.html18
68 files changed, 2386 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-1.html
new file mode 100644
index 0000000000..cadcf126f5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-1.html
@@ -0,0 +1,11 @@
+<!doctype html>
+001-1
+<script>
+addEventListener("pageshow",
+ function(e) {
+ parent.events.push(e);
+ if (parent.events.length == 2) {
+ parent.do_test();
+ }
+ }, false);
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-2.html
new file mode 100644
index 0000000000..6387bc89c8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001-2.html
@@ -0,0 +1,5 @@
+<!doctype html>
+001-2
+<script>
+onload = function() {setTimeout(function() {history.go(-1)}, 500)}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001.html
new file mode 100644
index 0000000000..336ede4cb6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/001.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<title>pageshow event from traversal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe src="001-1.html"></iframe>
+<script>
+var t = async_test();
+var events = [];
+var iframe = document.getElementsByTagName("iframe")[0];
+
+onload = t.step_func(function() {
+ setTimeout(t.step_func(
+ function() {
+ assert_equals(iframe.contentDocument.readyState, "complete")
+ iframe.src = "001-2.html";
+ }), 500);
+ onload = null;
+})
+
+do_test = t.step_func(function() {
+ assert_equals(events.length, 2);
+ events.forEach(function(e, i) {
+ phase = i ? "after" : "before";
+ assert_equals(e.type, "pageshow", "type " + phase + " navigation");
+
+ // https://github.com/whatwg/html/issues/6794
+ assert_equals(e.bubbles, true, "bubbles " + phase + " navigation");
+ assert_equals(e.cancelable, true, "cancelable " + phase + " navigation");
+
+ assert_equals(e.persisted, i == 0 ? false : true, "persisted " + phase + " navigation");
+ t.done();
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/PopStateEvent.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/PopStateEvent.html
new file mode 100644
index 0000000000..9ee7576249
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/PopStateEvent.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Synthetic popstate events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_throws_js(
+ TypeError,
+ () => PopStateEvent(''),
+ "Calling PopStateEvent constructor without 'new' must throw"
+ );
+}, "PopStateEvent constructor called as normal function");
+
+test(function () {
+ assert_false('initPopStateEvent' in PopStateEvent.prototype,
+ 'There should be no PopStateEvent#initPopStateEvent');
+}, 'initPopStateEvent');
+
+test(function () {
+ var popStateEvent = new PopStateEvent("popstate");
+ assert_equals(popStateEvent.state, null, "the PopStateEvent.state");
+}, "Initial value of PopStateEvent.state must be null");
+
+test(function () {
+ var popStateEvent = new PopStateEvent("popstate");
+ assert_false(popStateEvent.hasUAVisualTransition, "the PopStateEvent.hasUAVisualTransition");
+}, "Initial value of PopStateEvent.hasUAVisualTransition must be false");
+
+test(function () {
+ var state = history.state;
+ var data;
+ var hasUAVisualTransition = false;
+ window.addEventListener('popstate', function (e) {
+ data = e.state;
+ hasUAVisualTransition = e.hasUAVisualTransition;
+ });
+ window.dispatchEvent(new PopStateEvent('popstate', {
+ 'state': {testdata:true},
+ 'hasUAVisualTransition': true
+ }));
+ assert_true(data.testdata,'state data was corrupted');
+ assert_equals(history.state, state, "history.state was NOT set by dispatching the event");
+ assert_true(hasUAVisualTransition, 'hasUAVisualTransition not set correctly');
+}, 'Dispatching a synthetic PopStateEvent');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/api-availability.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/api-availability.html
new file mode 100644
index 0000000000..2f7d3fafdf
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/api-availability.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>API availability following history traversal</title>
+<meta charset=utf-8>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<p>Test requires popup blocker disabled</p>
+<div id=log></div>
+<script>
+var t = async_test();
+var hasNavigated = false;
+var child;
+t.step(function() {
+ child = window.open("resources/api-availability-1.html");
+ t.add_cleanup(function() {
+ child.close();
+ });
+});
+navigate = t.step_func(function() {
+ hasNavigated = true;
+ child.location = child.location.href.replace("api-availability-1.html", "api-availability-2.html");
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-0.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-0.html
new file mode 100644
index 0000000000..5cbab71a5e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-0.html
@@ -0,0 +1,35 @@
+<iframe id="test"></iframe>
+<script>
+var opener = window.opener;
+var t = opener.t;
+var f = document.getElementById("test");
+var l = opener.document.getElementById("step_log");
+
+log = function(t) {l.textContent += ("\n" + t)}
+var navigated = false;
+var steps = [
+ () => f.src = "browsing_context_name-1.html",
+ () => {
+ navigated = true;
+ opener.assert_equals(f.contentWindow.name, "test", "Initial load");
+ f.src = "browsing_context_name-2.html"
+ },
+ () => {
+ opener.assert_equals(f.contentWindow.name, "test1");
+ opener.assert_equals(history.length, 2);
+ history.back()
+ },
+ () => {
+ opener.assert_equals(f.contentWindow.name, "test1", "After navigation");
+ t.done();
+ }
+].map((x, i) => t.step_func(() => {log("Step " + (i+1)); x()}));
+
+next = () => steps.shift()();
+
+onload = () => {
+ log("page load");
+ f.onload = () => {log("iframe onload"); next()};
+ setTimeout(next, 0);
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-1.html
new file mode 100644
index 0000000000..85748a2ebc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-1.html
@@ -0,0 +1,6 @@
+document 1
+<script>
+if (!parent.navigated) {
+ window.name = "test";
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-2.html
new file mode 100644
index 0000000000..b0c869046b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-2.html
@@ -0,0 +1,4 @@
+document 2
+<script>
+window.name = "test1";
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-3.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-3.html
new file mode 100644
index 0000000000..e0c239744f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-3.html
@@ -0,0 +1,6 @@
+document 3
+<script>
+if (!parent.navigated) {
+ window.name = "test3";
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-4.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-4.html
new file mode 100644
index 0000000000..5d2dfa6bb8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name-4.html
@@ -0,0 +1,6 @@
+document 4
+<script>
+if (!parent.navigated) {
+ window.name = "test4";
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name.html
new file mode 100644
index 0000000000..60a8acb098
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>Retaining window.name on history traversal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<pre id="step_log"></pre>
+
+<script>
+var t = async_test();
+t.step(() => {
+ win = window.open("browsing_context_name-0.html");
+ t.add_cleanup(() => win.close());
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin-0.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin-0.html
new file mode 100644
index 0000000000..9e91722714
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin-0.html
@@ -0,0 +1,34 @@
+<iframe id="test"></iframe>
+<script>
+var t = opener.t;
+var f = document.getElementById("test");
+var l = opener.document.getElementById("step_log");
+
+var log = function(t) {l.textContent += ("\n" + t)}
+var navigated = false;
+var steps = [
+ () => f.src = "browsing_context_name-1.html",
+ () => {
+ navigated = true;
+ opener.assert_equals(f.contentWindow.name, "test", "Initial load");
+ f.src = f.src.replace("http://", "http://www.").replace("browsing_context_name-1", "browsing_context_name-2");
+ },
+ () => {
+ // Can't test .name easily here because it's cross-origin
+ opener.assert_equals(history.length, 2);
+ history.back()
+ },
+ () => {
+ opener.assert_equals(f.contentWindow.name, "test", "After navigation");
+ t.done();
+ }
+].map((x, i) => t.step_func(() => {log("Step " + (i+1)); x()}));
+
+next = () => steps.shift()();
+
+onload = () => {
+ log("page load");
+ f.onload = () => {log("iframe onload"); next()};
+ setTimeout(next, 0);
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin.html
new file mode 100644
index 0000000000..caa0bce3eb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>Restoring window.name on cross-origin history traversal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<pre id="step_log"></pre>
+
+<script>
+var t = async_test();
+t.step(() => {
+ var win = window.open("browsing_context_name_cross_origin-0.html");
+ t.add_cleanup(() => win.close());
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_2.html
new file mode 100644
index 0000000000..8202a892a3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_2.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<title>Restoring window.name on cross-origin history traversal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<pre id="step_log"></pre>
+<iframe id="test"></iframe>
+<script>
+var t = async_test();
+var f = document.getElementById("test");
+var l = document.getElementById("step_log");
+
+log = function(t) {l.textContent += ("\n" + t)}
+
+var steps = [
+ function() {f.src = "browsing_context_name-1.html"},
+ function() {
+ assert_equals(f.contentWindow.name, "test", "Initial load");
+ setTimeout(next, 0);
+ },
+ function() {f.src = "browsing_context_name-3.html"},
+ function() {
+ assert_equals(f.contentWindow.name, "test3", "After navigation 1");
+ setTimeout(next, 0);
+ },
+ function() {f.src = f.src.replace("http://", "http://www.").replace("browsing_context_name-3", "browsing_context_name-2");},
+ function() {
+ setTimeout(next, 0);
+ },
+ function() {history.go(-2); setTimeout(next, 500)},
+ function() {
+ assert_equals(f.contentWindow.name, "test3", "After navigation 2");
+ t.done();
+ }
+].map(function(x) {return t.step_func(function() {log("Step " + step + " " + f.contentWindow.location); x()})});
+
+var step = 0;
+next = t.step_func(function() {steps[step++]()});
+
+f.onload=next;
+
+onload = function() { setTimeout(next, 0); };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_3.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_3.html
new file mode 100644
index 0000000000..b6a35680dd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/browsing_context_name_cross_origin_3.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<title>Restoring window.name on cross-origin history traversal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<pre id="step_log"></pre>
+<iframe id="test"></iframe>
+<script>
+var t = async_test();
+var f = document.getElementById("test");
+var l = document.getElementById("step_log");
+
+log = function(t) {l.textContent += ("\n" + t)}
+
+var steps = [
+ function() {f.src = "browsing_context_name-1.html"},
+ function() {
+ assert_equals(f.contentWindow.name, "test", "Initial load");
+ setTimeout(next, 0);
+ },
+ function() {f.src = "browsing_context_name-3.html"},
+ function() {
+ assert_equals(f.contentWindow.name, "test3", "After navigation 1");
+ setTimeout(next, 0);
+ },
+ function() {f.src = f.src.replace("http://", "http://www.").replace("browsing_context_name-1", "browsing_context_name-2");},
+ function() {f.src = f.src.replace("http://www.", "http://").replace("browsing_context_name-2", "browsing_context_name-4");},
+ function() {
+ assert_equals(f.contentWindow.name, "test3", "After navigation 2");
+ history.go(-3); setTimeout(next, 500)
+ },
+ function() {
+ assert_equals(f.contentWindow.name, "test3", "After navigation 3");
+ t.done();
+ }
+].map(function(x) {return t.step_func(function() {log("Step " + step + " " + f.contentWindow.location); x()})});
+
+var step = 0;
+next = t.step_func(function() {steps[step++]()});
+
+f.onload=next;
+
+onload = function() { setTimeout(next, 0); };
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/document-state.https.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/document-state.https.html
new file mode 100644
index 0000000000..f03efc3b65
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/document-state.https.html
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test the properties of a session history entry's document state</title>
+<link rel="help" href="https://html.spec.whatwg.org/#document-state">
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+// In this test, we create an auxiliary window with a session history A -> B,
+// where the document on site B is the current active document. Bf-cache is
+// disabled via `Cache-Control: no-store` headers. We then `history.back()` to
+// site A, and perform `location.replace(B)`. This makes the first document in
+// the session history now same-origin/site with the URL in the subsequent
+// session history entry's document state.
+//
+// We then perform `history.forward()` in the first document, which loads the
+// second document (from network). We confirm that the resulting navigation
+// request was made with the expected state, stored on the history entry's
+// document state. The consequences of this are:
+// - The navigation is made with the `Sec-Fetch-Site: cross-site` header,
+// indicating that the *original* document state's initiator origin was
+// preserved
+// - The navigation is made with a cross-origin `Referer` header, indicating
+// that the *original* document state's referrer was preserved
+// - The resulting document has a cross-origin `document.referrer`, indicating
+// the same as above
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ const A = await rcHelper.addWindow();
+
+ // Create B on a new origin (with bf-cache disabled).
+ const B = await A.navigateToNew({
+ origin: 'HTTPS_NOTSAMESITE_ORIGIN',
+ headers: [['Cache-Control', 'no-store']],
+ });
+
+ // This is the origin we're going to navigate A to, so that it becomes
+ // same-origin with B.
+ const originB = new URL(await B.executeScript(() => location.href)).origin;
+ await B.historyBack();
+
+ // Make A navigate to the same document but in origin B:
+ const urlA = await A.executeScript(() => location.href);
+ const originA = new URL(urlA).origin;
+ assert_not_equals(originA, originB, 'Contexts A and B are cross-origin');
+
+ // Load A's current document but on origin B.
+ const newUrlOnOriginB = urlA.replace(originA, originB);
+ await A.navigate((url) => {
+ location.replace(url);
+ }, [newUrlOnOriginB]);
+
+ // Assert that A and B are now same-origin:
+ const newUrlA = await A.executeScript(() => {
+ return location.href;
+ });
+
+ // Now the session history looks like:
+ // B -> B (initiator origin: A)
+ assert_equals(new URL(newUrlA).origin, originB);
+
+ // This means that when we navigate forward, we should request the second
+ // document with the history entry's document state, which mostly preserves
+ // parameters from the original initiator (a cross-site document), despite a
+ // now-same-origin document initiating this navigation via history.
+ await A.historyForward();
+
+ const requestHeaders = await B.getRequestHeaders();
+ const documentReferrer = await B.executeScript(() => document.referrer);
+
+ assert_equals(requestHeaders.get('sec-fetch-site'), 'cross-site',
+ 'Same-origin forward history navigation to a document whose original ' +
+ 'initiator was cross-site, ends up with Sec-Fetch-Dest: cross-site ' +
+ 'header');
+ assert_equals(requestHeaders.get('referer'), originA + '/',
+ 'Same-origin forward history navigation to a document whose original ' +
+ 'initiator was cross-site ends up with the Referer header that is the ' +
+ 'original cross-site initiator');
+ assert_equals(documentReferrer, originA + '/',
+ 'Same-origin forward history navigation to a document whose original ' +
+ 'initiator was cross-site ends up with document.referrer that is the ' +
+ 'original cross-site initiator');
+}, "A navigation's initiator origin and referrer are stored in the document " +
+ "state and used in the document repopulation case");
+
+// This test is similar to the above, but instead of testing for the true
+// history entry -> document state -> document repopulation case, we stay on [B]
+// (the document who was navigated to from [A]) and run `location.reload()` to
+// confirm that the initiator information from the [A] -> [B] navigation is used
+// when reloading [B], not [B]'s own same-origin information.
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ const A = await rcHelper.addWindow();
+
+ const originA = new URL(await A.executeScript(() => location.href)).origin;
+
+ // Create B on a new origin.
+ const B = await A.navigateToNew({
+ origin: 'HTTPS_NOTSAMESITE_ORIGIN',
+ });
+
+ const originB = new URL(await B.executeScript(() => location.href)).origin;
+ assert_not_equals(originA, originB, 'Contexts A and B are cross-origin');
+
+ // Reload B.
+ await B.navigate(() => {
+ location.reload();
+ }, []);
+
+ const requestHeaders = await B.getRequestHeaders();
+ const documentReferrer = await B.executeScript(() => document.referrer);
+
+ assert_equals(requestHeaders.get('sec-fetch-site'), 'cross-site',
+ 'Same-origin forward history navigation to a document whose original ' +
+ 'initiator was cross-site, ends up with Sec-Fetch-Dest: cross-site ' +
+ 'header');
+ assert_equals(requestHeaders.get('referer'), originA + '/',
+ 'Same-origin forward history navigation to a document whose original ' +
+ 'initiator was cross-site ends up with the Referer header that is the ' +
+ 'original cross-site initiator');
+ assert_equals(documentReferrer, originA + '/',
+ 'Same-origin forward history navigation to a document whose original ' +
+ 'initiator was cross-site ends up with document.referrer that is the ' +
+ 'original cross-site initiator');
+}, "A navigation's initiator origin and referrer are stored in the document " +
+ "state and used on location.reload()");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash-twice.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash-twice.html
new file mode 100644
index 0000000000..75889ef517
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash-twice.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ // 0 timeout is necessary because if we do location.hash assignment before load is finished firing it counts as a replacement.
+ window.addEventListener("load", () => t.step_timeout(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+
+ window.addEventListener("hashchange", t.step_func(() => {
+ assert_array_equals(window.eventOrder, ["load", "popstate", "popstate", "hashchange"]);
+
+ window.addEventListener("hashchange", t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load", "popstate", "popstate", "hashchange", "hashchange"]);
+ }));
+ }), { once: true });
+
+ location.hash = "#1";
+ assert_array_equals(window.eventOrder, ["load", "popstate"]);
+ location.hash = "#2";
+ assert_array_equals(window.eventOrder, ["load", "popstate", "popstate"]);
+ }, 0));
+}, "when changing hash, after the load event");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash.html
new file mode 100644
index 0000000000..f74d716d91
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-hash.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ // 0 timeout is necessary because if we do location.hash assignment before load is finished firing it counts as a replacement.
+ window.addEventListener("load", () => t.step_timeout(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+
+ window.addEventListener("hashchange", t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load", "popstate", "hashchange"]);
+ }));
+
+ location.hash = "#1";
+ assert_array_equals(window.eventOrder, ["load", "popstate"]);
+ }, 0));
+}, "when changing hash, after the load event");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-pushState.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-pushState.html
new file mode 100644
index 0000000000..4f9f3dad47
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-pushState.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ // 0 timeout is necessary because if we do pushState before load is finished firing it counts as a replacement.
+ window.addEventListener("load", () => t.step_timeout(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+
+ t.step_timeout(t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+ }), 100);
+
+ history.pushState({ state: "new state" }, "");
+ }, 0));
+}, "when pushing state, after the load event");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-replaceState.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-replaceState.html
new file mode 100644
index 0000000000..28148ff7b2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/after-load-replaceState.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ window.addEventListener("load", t.step_func(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+
+ t.step_timeout(t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+ }), 100);
+
+ history.replaceState({ state: "new state" }, "");
+ }));
+}, "when replacing state, after the load event");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash-twice.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash-twice.html
new file mode 100644
index 0000000000..7c8df11843
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash-twice.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ window.addEventListener("load", t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["popstate", "popstate", "hashchange", "hashchange", "load"]);
+ }));
+
+ location.hash = "#1";
+ assert_array_equals(window.eventOrder, ["popstate"]);
+ location.hash = "#2";
+ assert_array_equals(window.eventOrder, ["popstate", "popstate"]);
+}, "when changing hash twice, before load");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash.html
new file mode 100644
index 0000000000..97c4636fad
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-hash.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ window.addEventListener("load", t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["popstate", "hashchange", "load"]);
+ }));
+
+ location.hash = "#1";
+ assert_array_equals(window.eventOrder, ["popstate"]);
+}, "when changing hash, before load");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-pushState.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-pushState.html
new file mode 100644
index 0000000000..a08afa474f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-pushState.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ window.addEventListener("load", t.step_func(() => {
+ t.step_timeout(t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+ }), 100);
+ }));
+
+ history.pushState({ state: "new state" }, "");
+}, "when pushing state, before load");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-replaceState.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-replaceState.html
new file mode 100644
index 0000000000..10d30038fb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/before-load-replaceState.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ t.step_timeout(t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+ }), 100);
+
+ history.replaceState({ state: "new state" }, "");
+}, "when replacing state, before load");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/pushState-inside-popstate.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/pushState-inside-popstate.html
new file mode 100644
index 0000000000..35ada116ed
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/pushState-inside-popstate.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(t => {
+ let popstate_called = false;
+ window.onpopstate = t.step_func(e => {
+ popstate_called = true;
+ history.pushState(2, null, "#2");
+ assert_not_equals(history.state, e.state);
+ });
+ location.hash = "#1";
+ assert_true(popstate_called);
+}, "pushState inside popstate")
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-immediate.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-immediate.html
new file mode 100644
index 0000000000..51ea20b289
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-immediate.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ // 0 timeout is necessary because if we do location.hash assignment before load is finished firing it counts as a replacement.
+ window.addEventListener("load", () => t.step_timeout(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+
+ window.addEventListener("hashchange", t.step_func(() => {
+ assert_array_equals(window.eventOrder, ["load", "popstate", "hashchange"]);
+
+ window.addEventListener("hashchange", t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load", "popstate", "hashchange", "popstate", "hashchange"]);
+ }));
+ }), { once: true });
+
+ location.hash = "#1";
+ assert_array_equals(window.eventOrder, ["load", "popstate"]);
+ history.back();
+ assert_array_equals(window.eventOrder, ["load", "popstate"]);
+ }, 0));
+}, "when traversing back, before hashchange");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-wait.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-wait.html
new file mode 100644
index 0000000000..39bc760ff7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/event-order/same-document-traverse-wait.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popstate/hashchange/load event ordering</title>
+
+<script>
+// Set these up super-early before we hit the network for the test harness, just in case.
+window.eventOrder = [];
+window.onhashchange = () => window.eventOrder.push("hashchange");
+window.onpopstate = () => window.eventOrder.push("popstate");
+window.onload = () => window.eventOrder.push("load");
+</script>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+ assert_array_equals(window.eventOrder, []);
+
+ // 0 timeout is necessary because if we do location.hash assignment before load is finished firing it counts as a replacement.
+ window.addEventListener("load", () => t.step_timeout(() => {
+ assert_array_equals(window.eventOrder, ["load"]);
+
+ window.addEventListener("hashchange", t.step_func(() => {
+ assert_array_equals(window.eventOrder, ["load", "popstate", "hashchange"]);
+
+ window.addEventListener("hashchange", t.step_func_done(() => {
+ assert_array_equals(window.eventOrder, ["load", "popstate", "hashchange", "popstate", "hashchange"]);
+ }));
+
+ history.back();
+ assert_array_equals(window.eventOrder, ["load", "popstate", "hashchange"]);
+ }), { once: true });
+
+ location.hash = "#1";
+ assert_array_equals(window.eventOrder, ["load", "popstate"]);
+ }, 0));
+}, "when traversing back, after hashchange");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/events.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/events.html
new file mode 100644
index 0000000000..d5ff83fac0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/events.html
@@ -0,0 +1,151 @@
+<!doctype html>
+<title> PageTransitionEffect Event </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var e = new PageTransitionEvent("pageshow", {persisted:false, cancelable:false, bubbles:false});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+ assert_false(e.persisted, "persisted");
+}, "Constructing pageshow event");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide", {persisted:false, cancelable:false, bubbles:false});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pagehide event");
+
+test(function() {
+ var e = new PageTransitionEvent("pageshow", {persisted:true});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_true(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pageshow event, persisted true");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide", {persisted:true});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_true(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pagehide event, persisted true");
+
+test(function() {
+ var e = new PageTransitionEvent("pageshow", {});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pageshow event, empty options");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide", {});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pagehide event, empty options");
+
+test(function() {
+ var e = new PageTransitionEvent("pageshow");
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pageshow event, missing options");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide");
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pagehide event, missing options");
+
+test(function() {
+ var e = new PageTransitionEvent("pageshow", {persisted:null});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pageshow event, persisted:null");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide", {persisted:null});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pagehide event, persisted:null");
+
+test(function() {
+ var e = new PageTransitionEvent("pageshow", {persisted:undefined});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pageshow event, persisted:undefined");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide", {persisted:undefined});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pagehide event, persisted:undefined");
+
+test(function() {
+ var e = new PageTransitionEvent("pageshow", {bubbles:true});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_false(e.persisted, "persisted");
+ assert_true(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pageshow event, bubbles:true");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide", {bubbles:true});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_false(e.persisted, "persisted");
+ assert_true(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+}, "Constructing pagehide event, bubbles:true");
+
+test(function() {
+ var e = new PageTransitionEvent("pageshow", {cancelable:true});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pageshow");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_true(e.cancelable, "cancelable");
+}, "Constructing pageshow event, cancelable:true");
+
+test(function() {
+ var e = new PageTransitionEvent("pagehide", {cancelable:true});
+ assert_true(e instanceof PageTransitionEvent);
+ assert_equals(e.type, "pagehide");
+ assert_false(e.persisted, "persisted");
+ assert_false(e.bubbles, "bubbles");
+ assert_true(e.cancelable, "cancelable");
+}, "Constructing pagehide event, cancelable:true");
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/hashchange_event.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/hashchange_event.html
new file mode 100644
index 0000000000..b7111255f8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/hashchange_event.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<title>Queue a task to fire hashchange event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+t = async_test();
+window.onload = t.step_func(function () {
+ if (location.href.toString().indexOf("#") > -1) {
+ location.href = location.href.replace(/#.*$/,'');
+ return;
+ }
+ var root = location.href;
+ var oldURLs = [];
+ var newURLs = [];
+
+ var timer = null;
+
+ location.hash = 'foo';
+ window.onhashchange = t.step_func(function (e) {
+ assert_true(e.isTrusted, "isTrusted");
+ assert_equals(e.target, window, "target");
+ assert_equals(e.type, "hashchange", "type");
+ assert_true(e instanceof HashChangeEvent, "is HashChangeEvent");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+ oldURLs.push(e.oldURL);
+ newURLs.push(e.newURL);
+ if (newURLs.length === 2) {
+ check_result();
+ } else if (timer === null) {
+ timer = setTimeout(function() {check_result()}, 500);
+ }
+ })
+
+ check_result = t.step_func(function() {
+ clearTimeout(timer);
+ try {
+ assert_array_equals([root, root+"#foo"], oldURLs, "e.newURL");
+ assert_array_equals([root+"#foo", root+"#bar"], newURLs, "e.newURL");
+ t.done();
+ } finally {
+ location.hash = "";
+ }
+ });
+
+ location.hash = 'bar';
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigate-parent-while-child-loading.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigate-parent-while-child-loading.html
new file mode 100644
index 0000000000..2b70375a14
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigate-parent-while-child-loading.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i"></iframe>
+<body>
+<script>
+async_test(t => {
+ let starting_history_length = history.length;
+ let iframe_url = (new URL("/common/blank.html", location.href)).href;
+ i.src = iframe_url;
+
+ history.pushState("a", "", "#a");
+ assert_equals(history.length, starting_history_length + 1, "First history length");
+
+ i.onload = t.step_func(() => {
+ assert_equals(history.length, starting_history_length + 1, "Second history length");
+ assert_equals(i.contentWindow.location.href, iframe_url);
+ assert_equals(location.hash, "#a");
+ history.back();
+ // Wait a while for a back navigation. Since all of the possible outcomes
+ // are either same-document or navigating to about:blank, this doesn't need
+ // to wait terribly long.
+ t.step_timeout(t.step_func_done(() => {
+ assert_equals(location.hash, "", "top frame should have navigated back");
+ assert_equals(i.contentWindow.location.href, iframe_url, "iframe should not have navigated");
+ }), 100);
+ });
+}, "pushState() in parent while child is doing initial navigation, then go back");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigates-multiple-frames.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigates-multiple-frames.html
new file mode 100644
index 0000000000..4f2429fbfd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/history-traversal-navigates-multiple-frames.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i" src="/common/blank.html"></iframe>
+<script>
+async_test(t => {
+ window.onload = () => t.step_timeout(t.step_func(() => {
+ let starting_history_length = history.length;
+ location.hash = "#a";
+ assert_equals(starting_history_length + 1, history.length);
+ i.contentWindow.location.hash = "#b";
+ assert_equals(starting_history_length + 2, history.length);
+
+ let popstateCount = 0;
+ const popstateCalled = t.step_func(() => {
+ popstateCount++;
+ if (popstateCount < 2)
+ return;
+ assert_equals(location.hash, "");
+ assert_equals(i.contentWindow.location.hash, "");
+ t.done();
+ });
+
+ window.onpopstate = popstateCalled;
+ i.contentWindow.onpopstate = popstateCalled;
+ history.go(-2);
+ }), 0);
+}, "A history traversal should be able to navigate a parent and child simultaneously");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-bfcache-restore.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-bfcache-restore.html
new file mode 100644
index 0000000000..f453c80a2a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-bfcache-restore.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<title>pagereveal event fires and in correct order on restoration from BFCache</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsing-the-web.html#updating-the-document">
+<link rel="author" href="mailto:bokan@chromium.org">
+<meta name="timeout" content="long">
+<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>
+// runBfcacheTest opens a popup to pageA which navigates to pageB and then
+// back, ensuring pageA is stored in the BFCache.
+runBfcacheTest({
+ funcBeforeNavigation: async () => {
+ // This function executes in pageA
+
+ // Wait for an animation frame to ensure the the initial-load
+ // `pagereveal` has already been fired so it doesn't get recorded
+ // below.
+ const raf = new Promise(resolve => requestAnimationFrame(resolve));
+ await raf;
+
+ window.event_log = [];
+ let restored = false;
+
+ function recordRafs() {
+ requestAnimationFrame( () => {
+ // Avoid recording animation frames until the page is restored from
+ // BFCache since it's currently uncached. This test is interested only
+ // in the behavior during restoration.
+ if (restored)
+ window.event_log.push('rAF');
+
+ recordRafs();
+ });
+ }
+
+ recordRafs();
+
+ addEventListener('pageshow', (e) => {
+ window.event_log.push('pageshow' + (e.persisted ? '.persisted' : ''));
+ if (e.persisted)
+ restored = true;
+ });
+
+ addEventListener('pagereveal', () => {
+ window.event_log.push('pagereveal');
+ });
+ },
+ funcAfterAssertion: async (pageA, pageB, t) => {
+ let event_log = await pageA.execute_script(async () => {
+ // Ensure at least one animation frame is produced to ensure
+ // pagereveal must have fired.
+ await new Promise(requestAnimationFrame);
+ return window.event_log;
+ });
+
+ // Expect that the events seen are:
+ // pageshow.persisted, pagereveal, rAF, rAF, rAF, ...
+ assert_equals(event_log.slice(0, 3).toString(),
+ 'pageshow.persisted,pagereveal,rAF');
+ for (let i = 3; i < event_log.length; ++i) {
+ assert_equals(event_log[i], 'rAF',
+ 'All events following pagereveal should be animation frames');
+ }
+ },
+ targetOrigin: originSameOrigin,
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-new-document-navigation.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-new-document-navigation.html
new file mode 100644
index 0000000000..d2c44511d3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-new-document-navigation.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>pagereveal event fires and in correct order on new-document navigation</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#update-the-rendering">
+<link rel="author" href="mailto:bokan@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const event_log = [];
+
+addEventListener('pageshow', () => event_log.push('pageshow'));
+addEventListener('pagereveal', () => event_log.push('pagereveal'));
+requestAnimationFrame(() => event_log.push('rAF'));
+
+promise_test(async () => {
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ assert_equals(event_log.toString(),'pageshow,pagereveal,rAF');
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-prerender-activation.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-prerender-activation.html
new file mode 100644
index 0000000000..b281b2b088
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/order-in-prerender-activation.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<title>pagereveal event fires and in correct order on prerender activation</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#update-the-rendering">
+<link rel="author" href="mailto:bokan@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/speculation-rules/resources/utils.js"></script>
+<script src="/speculation-rules/prerender/resources/utils.js"></script>
+<script>
+setup(() => assertSpeculationRulesIsSupported());
+
+const uid = token();
+const initiator_url = `resources/order-in-prerender-activation-popup.html?uid=${uid}`;
+
+// This test opens a popup to an initiator page. That page then prerenders a
+// "prerendering" version of itself (by adding a `prerendering` query param)
+// and navigates itself to activate the prerender. The results are recorded and
+// sent back to this test harness.
+promise_test(async () => {
+ const channel = new PrerenderChannel('result', uid);
+ const test_done = new Promise(resolve => {
+ channel.addEventListener('message', e => resolve(e.data), { once: true });
+ });
+
+ window.open(initiator_url, '_blank', 'noopener');
+
+ const result = await test_done;
+
+ if (result.hasOwnProperty('fail')) {
+ assert_unreached(result.fail);
+ }
+
+ // The test records relevant event occurrences up to the second animation
+ // frame. Ensure their order and apparance is as expected.
+ const events_in_order = result.events.join(',');
+ assert_equals(events_in_order,
+ 'pageshow,prerenderingchange,pagereveal,raf');
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/resources/order-in-prerender-activation-popup.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/resources/order-in-prerender-activation-popup.html
new file mode 100644
index 0000000000..78989adc17
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/pagereveal/resources/order-in-prerender-activation-popup.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<title>pagereveal event fires and in correct order on prerender activation (popup)</title>
+<link rel="author" href="mailto:bokan@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/speculation-rules/prerender/resources/utils.js"></script>
+<script>
+const params = new URLSearchParams(location.search);
+const uid = params.get('uid');
+const is_prerender_step = params.has('prerendering');
+
+const ready_channel = new PrerenderChannel('ready-to-activate', uid);
+
+function finish(result) {
+ const result_channel = new PrerenderChannel('result', uid);
+ result_channel.postMessage(result);
+ result_channel.close();
+ window.close();
+}
+
+// testharness.js assertions don't work inside this popup so this small helper
+// sends a failure signal back to the test page which will cause test failure.
+function assert(cond, desc) {
+ if (!cond) {
+ finish({fail: desc});
+ }
+}
+
+// The first load of this page should be without 'prerendering' and is used
+// to setup the prerender and then activate it when it's ready.
+if (!is_prerender_step) {
+ assert(!document.prerendering, 'initiator page must not be prerendered');
+
+ const ready_to_activate = new Promise(resolve => {
+ ready_channel.addEventListener('message', resolve, {once: true});
+ });
+
+ const prerendering_url = location.href + '&prerendering';
+ startPrerendering(prerendering_url);
+
+ ready_to_activate.then(() => {
+ location.replace(prerendering_url);
+ });
+} else {
+ assert(document.prerendering, 'prerendering step must be initially prerendered');
+
+ const result = {
+ events: []
+ };
+
+ document.addEventListener('prerenderingchange', () => {
+ result.events.push('prerenderingchange');
+ });
+
+ addEventListener('pageshow', () => {
+ result.events.push('pageshow');
+ });
+
+ // A second rAF will end the test.
+ requestAnimationFrame(() => {
+ result.events.push('raf');
+ requestAnimationFrame(() => finish(result));
+ });
+
+ addEventListener('pagereveal', () => {
+ result.events.push('pagereveal');
+ });
+
+ addEventListener('load', () => {
+ ready_channel.postMessage('unused-readyToActivateMessage');
+ ready_channel.close();
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank1.html
new file mode 100644
index 0000000000..6b4df1ef2f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<style>
+body {
+ height: 2000px;
+ width: 2000px;
+}
+</style>
+<body> Blank 1 </body> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank2.html
new file mode 100644
index 0000000000..def2139667
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<style>
+body {
+ height: 2000px;
+ width: 2000px;
+}
+</style>
+<body> Blank 2 </body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/page-with-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/page-with-fragment.html
new file mode 100644
index 0000000000..11737661d0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/page-with-fragment.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<style>
+body {
+ height: 2000px;
+ width: 2000px;
+}
+#fragment {
+ position: absolute;
+ top: 800px;
+ background-color: #faa;
+ display: block;
+ height: 100px;
+ width: 100px;
+}
+
+</style>
+<body>
+Page with fragment
+ <a id="fragment" name="fragment" class='box'></a>
+</body> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/post_name_on_load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/post_name_on_load.html
new file mode 100644
index 0000000000..1e9b10d1ee
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/post_name_on_load.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+addEventListener('load', _ => {
+ let params = new URLSearchParams(window.location.search);
+ window.opener.postMessage(params.get('name'), '*');
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resume-timer-on-history-back.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resume-timer-on-history-back.html
new file mode 100644
index 0000000000..77602b2d42
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resume-timer-on-history-back.html
@@ -0,0 +1,146 @@
+<!doctype html>
+<title>Verify history.back() on a persisted page resumes timers</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="text/javascript">
+
+function make_post_back_url(name) {
+ return new URL('resources/post_name_on_load.html?name=' + name,
+ window.location).href;
+}
+
+function wait_for_message(name) {
+ return new Promise(resolve => {
+ addEventListener('message', function onMsg(evt) {
+ if (evt.data !== name) {
+ return;
+ }
+ removeEventListener('message', onMsg);
+ resolve();
+ });
+ });
+}
+
+function with_window_by_name(name) {
+ let win = window.open(make_post_back_url(name));
+ return wait_for_message(name).then(_ => {
+ return win;
+ });
+}
+
+function with_nested_frame(win, url) {
+ return new Promise(resolve => {
+ let frame = win.document.createElement('iframe');
+ frame.addEventListener('load', function onLoad(evt) {
+ removeEventListener('load', onLoad);
+ resolve(frame);
+ });
+ frame.src = url;
+ win.document.body.appendChild(frame);
+ });
+}
+
+function delay(win, delay) {
+ return new Promise(resolve => {
+ win.setTimeout(_ => {
+ resolve(win);
+ }, delay);
+ });
+}
+
+function navigate_by_name(win, name) {
+ win.location = make_post_back_url(name);
+ return wait_for_message(name).then(_ => {
+ return win;
+ });
+}
+
+function go_back(win) {
+ return new Promise(resolve => {
+ win.onpagehide = e => resolve(win);
+ win.history.back();
+ });
+}
+
+let DELAY = 500;
+
+promise_test(t => {
+ // Create a new window so we can navigate it later.
+ return with_window_by_name('foo').then(win => {
+ // Schedule a timer within the new window. Our intent is
+ // to navigate the window before the timer fires.
+ let delayFired = false;
+ let innerDelay = delay(win, DELAY);
+ innerDelay.then(_ => {
+ delayFired = true;
+ });
+
+ return navigate_by_name(win, 'bar').then(_ => {
+ // Since the window has navigated the timer should not
+ // fire. We set a timer on our current test window
+ // to verify the other timer is not received.
+ assert_false(delayFired);
+ return delay(window, DELAY * 2);
+ }).then(_ => {
+ // The navigated window's timer should not have fired.
+ assert_false(delayFired);
+ // Now go back to the document that set the timer.
+ return go_back(win);
+ }).then(_ => {
+ // We wait for one of two conditions here. For browsers
+ // with a bfcache the original suspended timer will fire.
+ // Alternatively, if the browser reloads the page the original
+ // message will be sent again. Wait for either of these
+ // two events.
+ return Promise.race([wait_for_message('foo'), innerDelay]);
+ }).then(_ => {
+ win.close();
+ });
+ });
+}, 'history.back() handles top level page timer correctly');
+
+promise_test(t => {
+ let win;
+ // Create a new window so we can navigate it later.
+ return with_window_by_name('foo').then(w => {
+ win = w;
+
+ // Create a nested frame so we check if navigation and history.back()
+ // properly handle child window state.
+ return with_nested_frame(win, 'about:blank');
+
+ }).then(frame => {
+ // Schedule a timer within the nested frame contained by the new window.
+ // Our intent is to navigate the window before the timer fires.
+ let delayFired = false;
+ let innerDelay = delay(frame.contentWindow, DELAY);
+ innerDelay.then(_ => {
+ delayFired = true;
+ });
+
+ return navigate_by_name(win, 'bar').then(_ => {
+ // Since the window has navigated the timer should not
+ // fire. We set a timer on our current test window
+ // to verify the other timer is not received.
+ assert_false(delayFired);
+ return delay(window, DELAY * 2);
+ }).then(_ => {
+ // The navigated window's timer should not have fired.
+ assert_false(delayFired);
+ // Now go back to the document containing the frame that set the timer.
+ return go_back(win);
+ }).then(_ => {
+ // We wait for one of two conditions here. For browsers
+ // with a bfcache the original suspended timer will fire.
+ // Alternatively, if the browser reloads the page the original
+ // message will be sent again. Wait for either of these
+ // two events.
+ return Promise.race([wait_for_message('foo'), innerDelay]);
+ }).then(_ => {
+ win.close();
+ });
+ });
+}, 'history.back() handles nested iframe timer correctly');
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic.html
new file mode 100644
index 0000000000..e47cd9c383
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>Verify existence and basic read/write function of history.scrollRestoration</title>
+
+<style>
+ body {
+ height: 2000px;
+ width: 2000px;
+ }
+</style>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="text/javascript">
+ 'use strict';
+
+ test(function() {
+ assert_equals(history.scrollRestoration, 'auto');
+ }, 'Default value is "auto"');
+
+ test(function() {
+ history.scrollRestoration = 'manual';
+ assert_equals(history.scrollRestoration, 'manual', 'should be able to set "manual"');
+ history.scrollRestoration = 'auto';
+ assert_equals(history.scrollRestoration, 'auto', 'should be able to set "auto"');
+ }, 'It is writable');
+
+ test(function() {
+ history.scrollRestoration = 'auto';
+ for (var v of [3.1415, {}, 'bogus']) {
+ history.scrollRestoration = v;
+ assert_equals(history.scrollRestoration, 'auto', `setting to invalid value (${v}) should be ignored`);
+ }
+ }, 'Invalid values are ignored');
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html
new file mode 100644
index 0000000000..fec801e94b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<meta name=timeout content=long>
+<title>Precedence of scroll restoration mode over fragment scrolling in cross-origin history traversal</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<style>
+ iframe {
+ height: 300px;
+ width: 300px;
+ }
+</style>
+<div id="log"></div>
+<script>
+ 'use strict';
+
+ var next;
+ function frameOnload() {
+ if (next) {
+ next();
+ } else {
+ // The test does the following navigation steps for iframe
+ // 1. load page-with-fragment.html#fragment
+ // 2. load blank1
+ // 3. go back to page-with-fragment.html
+ async_test(function(t) {
+ var iframe = document.querySelector('iframe');
+ var hostInfo = get_host_info();
+ var basePath = location.pathname.substring(0, location.pathname.lastIndexOf('/'));
+ var localURL = hostInfo.HTTP_ORIGIN + basePath + '/resources/page-with-fragment.html#fragment';
+ var remoteURL = hostInfo.HTTP_REMOTE_ORIGIN + basePath + "/resources/blank1.html"
+
+ var steps = [
+ function() {
+ assert_equals(iframe.contentWindow.location.href, localURL, 'should be on page-with-fragment page');
+ // wait one animation frame to ensure layout is run and fragment scrolling is complete
+ iframe.contentWindow.requestAnimationFrame(function() {
+ assert_approx_equals(iframe.contentWindow.scrollY, 800, 5, 'should scroll to fragment');
+
+ iframe.contentWindow.history.scrollRestoration = 'manual';
+ assert_equals(iframe.contentWindow.history.scrollRestoration, 'manual');
+ setTimeout(next, 0);
+ });
+ }, function() {
+ // navigate to a new page from a different origin
+ iframe.src = remoteURL;
+ }, function() {
+ // going back causes the iframe to traverse back
+ history.back();
+ }, function() {
+ // coming back from history, scrollRestoration should be set to manual and respected
+ assert_equals(iframe.contentWindow.location.href, localURL, 'should be back on page-with-fragment page');
+ iframe.contentWindow.requestAnimationFrame(t.step_func_done(function() {
+ assert_equals(iframe.contentWindow.history.scrollRestoration, 'manual', 'navigating back should retain scrollRestoration value');
+ assert_equals(iframe.contentWindow.scrollX, 0, 'should not scroll to fragment');
+ assert_equals(iframe.contentWindow.scrollY, 0, 'should not scroll to fragment');
+ }));
+ }
+ ];
+
+ var stepCount = 0;
+ next = t.step_func(function() {
+ steps[stepCount++]();
+ });
+ next();
+ }, 'Manual scroll restoration should take precedent over scrolling to fragment in cross origin navigation');
+ }
+ }
+</script>
+<iframe src="resources/page-with-fragment.html#fragment" onload="frameOnload()"></iframe>
+
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-samedoc.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-samedoc.html
new file mode 100644
index 0000000000..073e0f6e06
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-samedoc.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<style>
+ body {
+ height: 2000px;
+ width: 2000px;
+ }
+
+ #fragment {
+ position: absolute;
+ top: 800px;
+ background-color: #faa;
+ display: block;
+ height: 100px;
+ width: 100px;
+ }
+</style>
+
+<body>
+ <a id="fragment" name="fragment" class='box'></a>
+</body>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="text/javascript">
+ 'use strict';
+
+ async_test(function(t) {
+ history.scrollRestoration = 'manual';
+ assert_equals(history.scrollRestoration, 'manual');
+
+ location.hash = '#fragment';
+ assert_equals(window.scrollY, 800, 'new navigations should scroll to fragment');
+
+ // create a new entry and reset the scroll before verification
+ history.pushState(null, null, '#done');
+ window.scrollTo(0, 0);
+ assert_equals(window.scrollY, 0, 'should reset scroll before verification');
+
+ setTimeout(function() {
+ // setup verification
+ window.addEventListener('hashchange', t.step_func(function() {
+ assert_equals(location.hash, '#fragment');
+ assert_equals(history.scrollRestoration, 'manual');
+ // navigating back should give precedent to history restoration which is 'manual'
+ assert_equals(window.scrollX, 0, 'should not scroll to fragment');
+ assert_equals(window.scrollY, 0, 'should not scroll to fragment');
+ t.done();
+ }));
+ // kick off verification
+ window.history.back();
+ }, 0);
+
+ }, 'Manual scroll restoration should take precedent over scrolling to fragment in cross doc navigation');
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin.html
new file mode 100644
index 0000000000..87a337b2da
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<meta name=timeout content=long>
+<title>Correct behaviour of scroll restoration mode is cross origin history traversal</title>
+
+<style>
+ iframe {
+ height: 300px;
+ width: 300px;
+ }
+</style>
+
+<body>
+ <iframe></iframe>
+</body>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="text/javascript">
+ 'use strict';
+
+ // The test does the following navigation steps for iframe
+ // 1. load blank1
+ // 2. load blank2
+ // 3. go back to blank1
+ async_test(function(t) {
+ var iframe = document.querySelector('iframe');
+ var baseURL = location.href.substring(0, location.href.lastIndexOf('/'));
+
+ var steps = [
+ function() {
+ iframe.src = 'resources/blank1.html';
+ },
+ function() {
+ assert_equals(iframe.contentWindow.location.href, baseURL + '/resources/blank1.html', 'should be on first blank page');
+ iframe.contentWindow.history.scrollRestoration = 'manual';
+ assert_equals(iframe.contentWindow.history.scrollRestoration, 'manual');
+ iframe.contentWindow.scrollTo(500, 500);
+ assert_equals(iframe.contentWindow.scrollX, 500, 'scripted scrolling should take effect');
+ assert_equals(iframe.contentWindow.scrollY, 500, 'scripted scrolling should take effect');
+ setTimeout(next, 0);
+ },
+ function() {
+ // navigate to new page
+ iframe.src = 'resources/blank2.html';
+ },
+ function() {
+ assert_equals(iframe.contentWindow.location.href, baseURL + '/resources/blank2.html', 'should be on second blank page');
+ assert_equals(iframe.contentWindow.history.scrollRestoration, 'auto', 'new page loads should set scrollRestoration to "auto"');
+ setTimeout(next, 0);
+ }, function() {
+ iframe.contentWindow.history.back();
+ }, function() {
+ // coming back scrollRestoration should be restored to 'manual' and respected
+ assert_equals(iframe.contentWindow.location.href, baseURL + '/resources/blank1.html', 'should be back on first blank page');
+ assert_equals(iframe.contentWindow.history.scrollRestoration, 'manual', 'navigating back should retain scrollRestoration value');
+ assert_equals(iframe.contentWindow.scrollX, 0, 'horizontal scroll offset should not be restored');
+ assert_equals(iframe.contentWindow.scrollY, 0, 'vertical scroll offset should not be restored');
+ t.done();
+ }
+ ];
+
+ var stepCount = 0;
+ var next = t.step_func(function() {
+ steps[stepCount++]();
+ });
+
+ iframe.onload = next;
+ next();
+ }, 'Navigating to new page should reset to "auto" and navigating back should restore and respect scroll restoration mode');
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html
new file mode 100644
index 0000000000..46d40eedc6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<title>Correct behaviour of scroll restoration mode in same document history traversals</title>
+
+<style>
+ body {
+ height: 10000px;
+ width: 10000px;
+ }
+</style>
+
+<body></body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="text/javascript">
+ 'use strict';
+
+ async_test(function(t) {
+ history.scrollRestoration = 'auto';
+ window.scrollTo(0, 0);
+
+ // create history entries and then verify the impact of scrollRestoration
+ // when they are popped
+ var entries = {
+ /* For scroll restoration mode 'auto', the spec does not require scroll
+ position to be restored at any particular value. */
+ '#1': {type: 'push', expectedScroll: null, scrollRestoration: 'auto'},
+ '#2': {type: 'replace', expectedScroll: null, scrollRestoration: 'auto'},
+ /* For scroll restoration mode 'manual', the spec requires scroll position
+ not to be restored. So we expect [555,555] which is the latest position
+ before navigation. */
+ '#3': {type: 'push', expectedScroll: [555, 555], scrollRestoration: 'manual'},
+ '#4': {type: 'replace', expectedScroll: [555, 555], scrollRestoration: 'manual'}
+ };
+
+ // setup entries
+ for (var key in entries) {
+ var entry = entries[key],
+ beforeValue = history.scrollRestoration,
+ newValue = entry.scrollRestoration;
+
+ var args = [{key: key}, '', key];
+ if (entry.type == 'push') {
+ history.pushState.apply(history, args);
+ } else {
+ history.pushState(null, '', key);
+ history.replaceState.apply(history, args);
+ }
+ assert_equals(history.scrollRestoration, beforeValue, `history.scrollRestoration value is retained after pushing new state`);
+ history.scrollRestoration = newValue;
+ assert_equals(history.scrollRestoration, newValue, `Setting scrollRestoration to ${newValue} works as expected`);
+ window.scrollBy(50, 100);
+ }
+
+ // setup verification
+ window.addEventListener('hashchange', t.step_func(function() {
+ var key = location.hash,
+ entry = entries[key];
+
+ if (key === '') {
+ t.done();
+ return;
+ }
+ assert_equals(history.state.key, key, `state should have key: ${key}`);
+ assert_equals(history.scrollRestoration, entry.scrollRestoration, 'scrollRestoration is updated correctly');
+ if (entry.expectedScroll) {
+ assert_equals(window.scrollX, entry.expectedScroll[0], `scrollX is correct for ${key}`);
+ assert_equals(window.scrollY, entry.expectedScroll[1], `scrollY is correct for ${key}`);
+ }
+
+ window.history.back();
+ }));
+
+ // reset the scroll and kick off the verification
+ setTimeout(function() {
+ history.pushState(null, null, '#done');
+ window.scrollTo(555, 555);
+ window.history.back();
+ }, 0);
+
+ }, 'history.{push,replace}State retain scroll restoration mode and navigation in the same document respects it');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/popstate_event.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/popstate_event.html
new file mode 100644
index 0000000000..05b1dbf4b7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/popstate_event.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<title>Queue a task to fire popstate event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+t = async_test();
+window.onload = t.step_func(function () {
+ var states = [];
+
+ var timer = null;
+
+ history.pushState("a", "State a", "/a");
+ history.pushState("b", "State b", "/b");
+
+ history.back();
+ window.onpopstate = t.step_func(function (e) {
+ assert_true(e.isTrusted, "isTrusted");
+ assert_equals(e.target, window, "target");
+ assert_equals(e.type, "popstate", "type");
+ assert_true(e instanceof PopStateEvent, "is PopStateEvent");
+ assert_false(e.bubbles, "bubbles");
+ assert_false(e.cancelable, "cancelable");
+ assert_not_equals(e.hasUAVisualTransition, undefined);
+
+ states.push(e.state);
+
+ if (states.length === 2) {
+ check_result();
+ } else if (timer === null) {
+ timer = setTimeout(function() {check_result()}, 500);
+ }
+ })
+
+ check_result = t.step_func(function() {
+ clearTimeout(timer);
+ try {
+ assert_array_equals(states, ["a", null]);
+ t.done();
+ } finally {
+ location.hash = "";
+ }
+ });
+
+ setTimeout(function() {history.back()}, 0);
+
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/a.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/a.html
new file mode 100644
index 0000000000..55b73e1153
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/a.html
@@ -0,0 +1 @@
+Welcome to A. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-1.html
new file mode 100644
index 0000000000..2c31168750
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-1.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>API availability following history traversal - 1</title>
+<script>
+var controller = opener;
+var t = controller.t;
+var assert_not_equals = controller.assert_not_equals;
+
+t.step(function() {
+ // If this document is discarded as a result of navigation, then this script
+ // will be executed a second time. The semantics this test intends to verify
+ // cannot be observed under these conditions, the discarding is not itself a
+ // violation. Silently pass the test in that case.
+ if (controller.hasNavigated) {
+ t.done();
+ return;
+ }
+
+ t.step_timeout(function() {
+ assert_not_equals(window.history, null, 'history');
+ assert_not_equals(window.localStorage, null, 'localStorage');
+ assert_not_equals(window.location, null, 'location');
+ assert_not_equals(window.navigator, null, 'navigator');
+ assert_not_equals(window.opener, null, 'opener');
+ assert_not_equals(window.sessionStorage, null, 'sessionStorage');
+
+ t.done();
+ }, 1000);
+
+ controller.navigate();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-2.html
new file mode 100644
index 0000000000..420e5092bc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/api-availability-2.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>API availability following history traversal - 2</title>
+<body onload="history.back()"></body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/b.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/b.html
new file mode 100644
index 0000000000..8f2fc900dd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/b.html
@@ -0,0 +1 @@
+Welcome to B. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/c.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/c.html
new file mode 100644
index 0000000000..db494e878e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/c.html
@@ -0,0 +1 @@
+Welcome to C. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name-1.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name-1.sub.html
new file mode 100644
index 0000000000..97918a1f99
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name-1.sub.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<!-- test must be run in a top level browsing context -->
+<title>window.name test helper</title>
+<script>
+const search = window.location.search.replace("?", "");
+const steps = search.split("|");
+
+async function proceedTest() {
+ while (steps.length) {
+ const step = steps.shift();
+
+ if (step.startsWith("report=")) {
+ const id = step.split("=")[1];
+ const stashURL = new URL("unset_context_name_stash.py", location);
+ stashURL.searchParams.set('id', id);
+ stashURL.searchParams.set('value', window.name);
+
+ await fetch(stashURL, { method: "POST" });
+ continue;
+ }
+
+ if (step === "close") {
+ window.close();
+ break;
+ }
+
+ if (step === "navigate") {
+ const url = new URL(window.location);
+ url.host = "{{hosts[][www]}}:{{ports[http][0]}}";
+ url.search = "?" + steps.join("|");
+ window.location = url.href;
+ break;
+ }
+
+ if (step.startsWith("set=")) {
+ window.name = step.split("=")[1];
+ continue;
+ }
+
+ throw new Error("Unsupported step!");
+ }
+}
+
+proceedTest();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name_stash.py b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name_stash.py
new file mode 100644
index 0000000000..411a4587bc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/resources/unset_context_name_stash.py
@@ -0,0 +1,13 @@
+def main(request, response):
+ key = request.GET.first(b"id")
+ if request.method == "POST":
+ value = request.GET.first(b"value")
+ request.server.stash.take(key)
+ request.server.stash.put(key, value)
+ return b"OK"
+ else:
+ value = request.server.stash.take(key)
+ if value is not None:
+ return value
+ else:
+ return b"NONE"
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/same-url.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/same-url.html
new file mode 100644
index 0000000000..bcca5ed90c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/same-url.html
@@ -0,0 +1,50 @@
+<title>Test same-URL navigation and its effects on history</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<iframe src=resources/a.html></iframe>
+<script>
+async_test((t) => {
+ let state = "begin"
+ self[0].frameElement.onload = t.step_func(() => {
+ if(state === "b first") {
+ assert_equals(history.length, 2)
+
+ state = "c first"
+ navigateFrameAfterDelay(t, "resources/c.html")
+ } else if (state === "c first") {
+ assert_equals(history.length, 3)
+
+ state = "a second"
+ history.back(2)
+ } else if (state === "a second") {
+ assert_equals(history.length, 3)
+
+ state = "a third"
+ navigateFrameAfterDelay(t, "resources/a.html")
+ } else if (state === "a third") {
+ assert_equals(history.length, 3)
+ t.done()
+ }
+ })
+ onload = t.step_func(() => {
+ assert_equals(state, "begin")
+ assert_equals(history.length, 1)
+
+ state = "b first"
+
+ navigateFrameAfterDelay(t, "resources/b.html")
+ })
+})
+
+function navigateFrameAfterDelay(t, url) {
+ // Delay to avoid triggering the "replace" behavior which occurs if
+ // the page isn't yet completely loaded, which only occurs after the
+ // load event handlers have finished:
+ // https://html.spec.whatwg.org/#location-object-navigate
+ // https://html.spec.whatwg.org/#the-end:completely-finish-loading
+ t.step_timeout(() => {
+ self[0].location = url
+ }, 0)
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/scroll-restoration-order.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/scroll-restoration-order.html
new file mode 100644
index 0000000000..8fe7d9f977
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/scroll-restoration-order.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>History restoration order test</title>
+<meta name="assert" content="https://html.spec.whatwg.org/multipage/browsing-the-web.html#history-traversal">
+<meta name="assert" content="Traversing history should restore scroll position after dispatching popstate and before dispatching hashchange">
+
+<style>
+ body {
+ height: 200vh;
+ width: 200vw;
+ }
+</style>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ 'use strict';
+ async_test(function(t) {
+ window.addEventListener('load', t.step_func(function() {
+ // Allow 1px epsilon for fractional scrolling.
+ assert_array_approx_equals(scrollPosition(), [0, 0], 1);
+
+ history.pushState('#1', '', '#1');
+ window.scrollTo(50, 100);
+ assert_array_approx_equals(scrollPosition(), [50, 100], 1);
+
+ history.pushState('#2', '', '#2');
+ window.scrollTo(100, 200);
+ assert_array_approx_equals(scrollPosition(), [100, 200], 1);
+
+ setTimeout(t.step_func(function(){
+ history.pushState(null, null, '#done');
+ window.scrollTo(555, 555);
+ assert_array_approx_equals(scrollPosition(), [555, 555], 1);
+ // Kick off the verification.
+ window.history.back();
+ }), 0);
+ }));
+
+ window.addEventListener('popstate', t.step_func(function() {
+ // Verify that scroll position is *not* restored before popstate.
+ const key = location.hash;
+ const expected_scroll_position = expectedScrollPositionForKey(key);
+ assert_not_equals(scrollPosition()[0], expected_scroll_position[0], `scroll is restored before popstate for ${key}`);
+ assert_not_equals(scrollPosition()[1], expected_scroll_position[1], `scroll is restored before popstate for ${key}`);
+
+ if (key == '')
+ t.done();
+ else
+ setTimeout(t.step_func(function(){ window.history.back(); }), 0);
+ }));
+
+ window.addEventListener('hashchange', t.step_func(function() {
+ // Verify that scroll position is restored before hashchange.
+ var key = location.hash;
+ const expected_scroll_position = expectedScrollPositionForKey(key);
+ assert_array_approx_equals(scrollPosition(), expected_scroll_position, 1, `scroll is restored before hashchange for ${key}`);
+ }));
+
+ function scrollPosition() {
+ return [window.pageXOffset, window.pageYOffset];
+ }
+
+ function expectedScrollPositionForKey(key) {
+ switch (key) {
+ case '#2': return [100, 200];
+ case '#1': return [50, 100];
+ case '' : return [0, 0];
+ default: assert_unreached();
+ }
+ }
+
+ }, 'Traversing history should restore scroll position after dispatching popstate and before dispatching hashchange');
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/consecutive-srcdoc.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/consecutive-srcdoc.html
new file mode 100644
index 0000000000..9aab36b986
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/consecutive-srcdoc.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>changing srcdoc to a different srcdoc</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/6809#issuecomment-905677979">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/helpers.js"></script>
+
+<script>
+"use strict";
+
+// Note: bfcache won't mess with any windows with openers, so it doesn't
+// interfere with these tests.
+
+promise_test(async t => {
+ // Set up a window whose iframe contains [normal page, srcdoc] entries.
+ const w = await openWindow("../../resources/has-iframe.html", t);
+ const iframe = w.document.querySelector("iframe");
+
+ await waitToAvoidReplace(t);
+ iframe.srcdoc = srcdocThatPostsParentOpener("srcdoc1");
+ await waitForMessage(iframe.contentWindow);
+
+ assert_equals(w.history.length, 2);
+
+ // Now navigate to a different srcdoc
+ iframe.srcdoc = srcdocThatPostsParentOpener("srcdoc2");
+
+ // Test that it's a replace.
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(w.history.length, 2,
+ "history.length must not change since it was a replace");
+ assert_equals(
+ iframe.contentDocument.querySelector("p").textContent,
+ "srcdoc2",
+ "Sanity check: the srcdoc document did indeed update"
+ );
+}, "changing srcdoc does a replace navigation since the URL is still " +
+ "about:srcdoc");
+
+promise_test(async t => {
+ // Set up a window whose iframe contains [normal page, srcdoc] entries.
+ const w = await openWindow("../../resources/has-iframe.html", t);
+ const iframe = w.document.querySelector("iframe");
+
+ await waitToAvoidReplace(t);
+ iframe.srcdoc = srcdocThatPostsParentOpener("srcdoc1");
+ await waitForMessage(iframe.contentWindow);
+
+ assert_equals(w.history.length, 2);
+
+ // Now navigate to about:srcdoc#yo
+ iframe.contentWindow.location.href = "about:srcdoc#yo";
+ assert_equals(iframe.contentWindow.location.href, "about:srcdoc#yo");
+ assert_equals(w.history.length, 3);
+
+ // Now navigate to a different srcdoc
+ iframe.srcdoc = srcdocThatPostsParentOpener("srcdoc2");
+
+ // Test that it's a push back to about:srcdoc.
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(
+ w.history.length,
+ 4,
+ "history.length must increase since it was a push"
+ );
+ assert_equals(iframe.contentWindow.location.href, "about:srcdoc");
+ assert_equals(
+ iframe.contentDocument.querySelector("p").textContent,
+ "srcdoc2",
+ "Sanity check: the srcdoc document did indeed update"
+ );
+
+ // Test that we can go back to about:srcdoc#yo.
+ w.history.back();
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.href, "about:srcdoc#yo");
+ assert_equals(
+ iframe.contentDocument.querySelector("p").textContent,
+ "srcdoc1",
+ "srcdoc content must be restored from history"
+ );
+}, "changing srcdoc to about:srcdoc#yo then another srcdoc does two push " +
+ "navigations and we can navigate back");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/srcdoc-history-entries.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/srcdoc-history-entries.html
new file mode 100644
index 0000000000..09f4094c5f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/srcdoc/srcdoc-history-entries.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>srcdoc history entries</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/6809">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/helpers.js"></script>
+
+<script>
+"use strict";
+
+// Note: bfcache won't mess with any windows with openers, so it doesn't
+// interfere with these tests.
+
+promise_test(async t => {
+ // Set up a window whose iframe contains
+ // [normal page, srcdoc, normal page, srcdoc] entries.
+ const w = await openWindow("/common/blank.html", t);
+ const iframe = await addIframe("/common/blank.html?iframe", w.document);
+
+ assert_equals(w.history.length, 1);
+
+ await waitToAvoidReplace(t);
+ iframe.srcdoc = srcdocThatPostsParentOpener("srcdoc1");
+ assert_equals(w.history.length, 1, "srcdoc navigation must not be sync");
+
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(w.history.length, 2);
+
+ await waitToAvoidReplace(t);
+ const middleURL = (new URL(
+ "../../resources/post-top-opener-on-load.html", location.href)).href;
+ iframe.contentWindow.location.href = middleURL;
+
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(w.history.length, 3);
+
+ await waitToAvoidReplace(t);
+ iframe.srcdoc = srcdocThatPostsParentOpener("srcdoc2");
+ assert_equals(w.history.length, 3, "srcdoc navigation must not be sync");
+
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(w.history.length, 4);
+
+ // Now test traversal.
+ w.history.back();
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.href, middleURL);
+
+ await waitToAvoidReplace(t);
+
+ w.history.back();
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(iframe.contentWindow.location.href, "about:srcdoc");
+ assert_equals(
+ iframe.contentDocument.querySelector("p").textContent,
+ "srcdoc1",
+ "srcdoc contents must be restored from history, not from the current " +
+ "value ('srcdoc2') of the content attribute"
+ );
+}, "srcdoc history entries: the iframe itself navigates");
+
+promise_test(async t => {
+ // Set up a window whose iframe contains [normal page, srcdoc] entries.
+ const w = await openWindow("../../resources/has-iframe.html", t);
+ const iframe = w.document.querySelector("iframe");
+
+ assert_equals(w.history.length, 1);
+
+ await waitToAvoidReplace(t);
+ iframe.srcdoc = srcdocThatPostsParentOpener("srcdoc1");
+ assert_equals(w.history.length, 1, "srcdoc navigation must not be sync");
+
+ await waitForMessage(iframe.contentWindow);
+ assert_equals(w.history.length, 2);
+
+ // Now navigate the window itself.
+ w.location.href = "../../resources/post-top-opener-on-load.html";
+ await waitForMessage(w);
+ assert_equals(w.history.length, 3);
+
+ // Now test traversal.
+ w.history.back();
+ await waitForMessage(w);
+ const iframeAgain = w.document.querySelector("iframe");
+
+ assert_equals(iframeAgain.contentWindow.location.href, "about:srcdoc");
+ assert_equals(
+ iframeAgain.contentDocument?.querySelector("p")?.textContent,
+ "srcdoc1",
+ "srcdoc contents must be restored from history, not from the current " +
+ "value of the (not-existing) content attribute"
+ );
+}, "srcdoc history entries: the container window navigates");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-cross-origin-main-frame-navigation-popup.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-cross-origin-main-frame-navigation-popup.sub.html
new file mode 100644
index 0000000000..e13d191658
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-cross-origin-main-frame-navigation-popup.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+<body>
+ <script>
+ document.location = "window-name-navigation.sub.html?hostname={{domains[www1]}}&shouldhavename=false&sendmessage=true";
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-same-origin-main-frame-navigation-1.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-same-origin-main-frame-navigation-1.sub.html
new file mode 100644
index 0000000000..4b7824b488
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-after-same-origin-main-frame-navigation-1.sub.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+ window.location = "window-name-navigation.sub.html?hostname={{host}}&shouldhavename=true&sendmessage=true";
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-navigation.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-navigation.sub.html
new file mode 100644
index 0000000000..285469a148
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-navigation.sub.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+ <script>
+ var url = new URL(window.location.href);
+ url.hostname = "{{GET[hostname]}}";
+ url.pathname = "/html/browsers/browsing-the-web/history-traversal/support/window-name-test.sub.html";
+ url.search = "shouldhavename={{GET[shouldhavename]}}&sendmessage={{GET[sendmessage]}}";
+ window.name = "test";
+ window.location = url.href;
+ </script>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-test.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-test.sub.html
new file mode 100644
index 0000000000..460db2e979
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/support/window-name-test.sub.html
@@ -0,0 +1,23 @@
+<script>
+ function process_test_result(passed, test_name) {
+ if ({{GET[sendmessage]}}) {
+ if (window.opener) {
+ window.opener.postMessage(passed, "*");
+ } else {
+ parent.postMessage(passed, "*");
+ }
+ } else {
+ let host = window.opener || parent;
+ host.test(function(t) {
+ host.assert_equals(passed, true);
+ }, test_name);
+ host.done();
+ }
+ }
+
+ if ({{GET[shouldhavename]}}) {
+ process_test_result(window.name == "test", "Test that window name is present");
+ } else {
+ process_test_result(window.name == "", "Test that window name is not present");
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/unset_context_name.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/unset_context_name.html
new file mode 100644
index 0000000000..285f6d7428
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/unset_context_name.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>window.name is reset after navigating to a different origin</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script>
+
+async function pollResultAndCheck(t, id, expected) {
+ const stashURL = new URL("resources/unset_context_name_stash.py", location);
+ stashURL.searchParams.set('id', id);
+
+ let res = "NONE";
+ while (res == "NONE") {
+ await new Promise(resolve => { t.step_timeout(resolve, 100); });
+
+ const response = await fetch(stashURL);
+ res = await response.text();
+ }
+ if (res !== expected) {
+ assert_unreached('Stash result does not equal expected result.')
+ }
+}
+
+promise_test(async t => {
+ const id = token();
+
+ window.open(`resources/unset_context_name-1.sub.html?set=${id}|navigate|report=${id}|close`, "_blank", "noopener");
+ await pollResultAndCheck(t, id, "");
+}, "Window.name is reset after navigating to a different origin");
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-aux-frame-navigation.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-aux-frame-navigation.sub.html
new file mode 100644
index 0000000000..6bc64c6373
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-aux-frame-navigation.sub.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <!-- window.name should equal "test" after a cross-origin auxiliary frame navigation. -->
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+ <script>
+ t = async_test("Test that the window name is correct");
+ window.addEventListener("message", t.step_func_done(function(e) {
+ assert_equals(e.data, true);
+ }));
+ window.open("support/window-name-navigation.sub.html?hostname={{domains[www1]}}&shouldhavename=true&sendmessage=true");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-main-frame-navigation.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-main-frame-navigation.sub.html
new file mode 100644
index 0000000000..fb0bb1883f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-main-frame-navigation.sub.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+ <button id="button" onclick="popup();">open popup</button>
+ <script>
+ function popup() {
+ window.popupWin = window.open('support/window-name-after-cross-origin-main-frame-navigation-popup.sub.html', '_blank');
+ }
+ async_test(t => {
+ t.add_cleanup(() => {
+ popupWin.close();
+ })
+ document.getElementById('button').click();
+ onmessage = t.step_func(e => {
+ assert_true(e.data);
+ });
+ }, 'window.name should equal "" after a cross-origin main frame navigation');
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-sub-frame-navigation.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-sub-frame-navigation.sub.html
new file mode 100644
index 0000000000..a309be6d80
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-cross-origin-sub-frame-navigation.sub.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <!-- window.name should equal "test" after a cross-origin sub frame navigation. -->
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+ <script>
+ t = async_test("Test that the window name is correct");
+ window.addEventListener("message", t.step_func_done(function(e) {
+ assert_equals(e.data, true);
+ }));
+ </script>
+ <iframe src="support/window-name-navigation.sub.html?hostname={{domains[www1]}}&shouldhavename=true&sendmessage=true";
+ </iframe>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-aux-frame-navigation.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-aux-frame-navigation.sub.html
new file mode 100644
index 0000000000..8e0a95d8c0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-aux-frame-navigation.sub.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <!-- window.name should equal "test" after a same-origin auxiliary frame navigation. -->
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+ <script>
+ t = async_test("Test that the window name is correct");
+ window.addEventListener("message", t.step_func_done(function(e) {
+ assert_equals(e.data, true);
+ }));
+ window.open("support/window-name-navigation.sub.html?hostname={{host}}&shouldhavename=true&sendmessage=true");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-main-frame-navigation.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-main-frame-navigation.html
new file mode 100644
index 0000000000..ef11d9971a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-main-frame-navigation.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<title>window.name after a same-origin main frame navigation</title>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<body>
+ <script>
+ var win;
+ async_test(function(t) {
+ win = window.open("support/window-name-after-same-origin-main-frame-navigation-1.sub.html")
+ addEventListener("message", t.step_func_done(e => assert_true(e.data)));
+ }).add_cleanup(() => {if (win) {win.close()}});
+ </script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-sub-frame-navigation.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-sub-frame-navigation.sub.html
new file mode 100644
index 0000000000..48a6e247b0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/window-name-after-same-origin-sub-frame-navigation.sub.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <!-- window.name should equal "test" after a same-origin sub frame navigation. -->
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+ <script>
+ t = async_test("Test that the window name is correct");
+ window.addEventListener("message", t.step_func_done(function(e) {
+ assert_equals(e.data, true);
+ }));
+ </script>
+ <iframe src="support/window-name-navigation.sub.html?hostname={{host}}&shouldhavename=true&sendmessage=true";
+ </iframe>
+</body>
+</html>