summaryrefslogtreecommitdiffstats
path: root/docshell/test/navigation
diff options
context:
space:
mode:
Diffstat (limited to 'docshell/test/navigation')
-rw-r--r--docshell/test/navigation/NavigationUtils.js203
-rw-r--r--docshell/test/navigation/blank.html1
-rw-r--r--docshell/test/navigation/bluebox_bug430723.html6
-rw-r--r--docshell/test/navigation/browser.ini23
-rw-r--r--docshell/test/navigation/browser_bug1757458.js46
-rw-r--r--docshell/test/navigation/browser_bug343515.js276
-rw-r--r--docshell/test/navigation/browser_ghistorymaxsize_is_0.js82
-rw-r--r--docshell/test/navigation/browser_test-content-chromeflags.js54
-rw-r--r--docshell/test/navigation/browser_test_bfcache_eviction.js102
-rw-r--r--docshell/test/navigation/browser_test_shentry_wireframe.js128
-rw-r--r--docshell/test/navigation/browser_test_simultaneous_normal_and_history_loads.js57
-rw-r--r--docshell/test/navigation/bug343515_pg1.html5
-rw-r--r--docshell/test/navigation/bug343515_pg2.html7
-rw-r--r--docshell/test/navigation/bug343515_pg3.html7
-rw-r--r--docshell/test/navigation/bug343515_pg3_1.html6
-rw-r--r--docshell/test/navigation/bug343515_pg3_1_1.html1
-rw-r--r--docshell/test/navigation/bug343515_pg3_2.html1
-rw-r--r--docshell/test/navigation/cache_control_max_age_3600.sjs20
-rw-r--r--docshell/test/navigation/file_beforeunload_and_bfcache.html31
-rw-r--r--docshell/test/navigation/file_blockBFCache.html33
-rw-r--r--docshell/test/navigation/file_bug1300461.html61
-rw-r--r--docshell/test/navigation/file_bug1300461_back.html37
-rw-r--r--docshell/test/navigation/file_bug1300461_redirect.html10
-rw-r--r--docshell/test/navigation/file_bug1300461_redirect.html^headers^2
-rw-r--r--docshell/test/navigation/file_bug1326251.html212
-rw-r--r--docshell/test/navigation/file_bug1326251_evict_cache.html17
-rw-r--r--docshell/test/navigation/file_bug1364364-1.html33
-rw-r--r--docshell/test/navigation/file_bug1364364-2.html14
-rw-r--r--docshell/test/navigation/file_bug1375833-frame1.html8
-rw-r--r--docshell/test/navigation/file_bug1375833-frame2.html8
-rw-r--r--docshell/test/navigation/file_bug1375833.html22
-rw-r--r--docshell/test/navigation/file_bug1379762-1.html35
-rw-r--r--docshell/test/navigation/file_bug1536471.html8
-rw-r--r--docshell/test/navigation/file_bug1583110.html26
-rw-r--r--docshell/test/navigation/file_bug1609475.html51
-rw-r--r--docshell/test/navigation/file_bug1706090.html40
-rw-r--r--docshell/test/navigation/file_bug1745638.html15
-rw-r--r--docshell/test/navigation/file_bug1750973.html45
-rw-r--r--docshell/test/navigation/file_bug1758664.html32
-rw-r--r--docshell/test/navigation/file_bug386782_contenteditable.html1
-rw-r--r--docshell/test/navigation/file_bug386782_designmode.html1
-rw-r--r--docshell/test/navigation/file_bug462076_1.html55
-rw-r--r--docshell/test/navigation/file_bug462076_2.html52
-rw-r--r--docshell/test/navigation/file_bug462076_3.html52
-rw-r--r--docshell/test/navigation/file_bug508537_1.html33
-rw-r--r--docshell/test/navigation/file_bug534178.html30
-rw-r--r--docshell/test/navigation/file_contentpolicy_block_window.html5
-rw-r--r--docshell/test/navigation/file_docshell_gotoindex.html42
-rw-r--r--docshell/test/navigation/file_document_write_1.html18
-rw-r--r--docshell/test/navigation/file_evict_from_bfcache.html29
-rw-r--r--docshell/test/navigation/file_fragment_handling_during_load.html27
-rw-r--r--docshell/test/navigation/file_fragment_handling_during_load_frame1.html6
-rw-r--r--docshell/test/navigation/file_fragment_handling_during_load_frame2.sjs20
-rw-r--r--docshell/test/navigation/file_load_history_entry_page_with_one_link.html7
-rw-r--r--docshell/test/navigation/file_load_history_entry_page_with_two_links.html9
-rw-r--r--docshell/test/navigation/file_meta_refresh.html40
-rw-r--r--docshell/test/navigation/file_navigation_type.html25
-rw-r--r--docshell/test/navigation/file_nested_frames.html27
-rw-r--r--docshell/test/navigation/file_nested_frames_innerframe.html1
-rw-r--r--docshell/test/navigation/file_nested_srcdoc.html3
-rw-r--r--docshell/test/navigation/file_new_shentry_during_history_navigation_1.html5
-rw-r--r--docshell/test/navigation/file_new_shentry_during_history_navigation_1.html^headers^1
-rw-r--r--docshell/test/navigation/file_new_shentry_during_history_navigation_2.html10
-rw-r--r--docshell/test/navigation/file_new_shentry_during_history_navigation_2.html^headers^1
-rw-r--r--docshell/test/navigation/file_new_shentry_during_history_navigation_3.html22
-rw-r--r--docshell/test/navigation/file_new_shentry_during_history_navigation_3.html^headers^1
-rw-r--r--docshell/test/navigation/file_new_shentry_during_history_navigation_4.html16
-rw-r--r--docshell/test/navigation/file_online_offline_bfcache.html41
-rw-r--r--docshell/test/navigation/file_reload.html23
-rw-r--r--docshell/test/navigation/file_reload_large_postdata.sjs46
-rw-r--r--docshell/test/navigation/file_reload_nonbfcached_srcdoc.sjs27
-rw-r--r--docshell/test/navigation/file_same_url.html24
-rw-r--r--docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache.html30
-rw-r--r--docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html35
-rw-r--r--docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html^headers^1
-rw-r--r--docshell/test/navigation/file_scrollRestoration_navigate.html17
-rw-r--r--docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html63
-rw-r--r--docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html^headers^1
-rw-r--r--docshell/test/navigation/file_scrollRestoration_part2_bfcache.html57
-rw-r--r--docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html157
-rw-r--r--docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html^headers^1
-rw-r--r--docshell/test/navigation/file_session_history_on_redirect.html16
-rw-r--r--docshell/test/navigation/file_session_history_on_redirect.html^headers^1
-rw-r--r--docshell/test/navigation/file_session_history_on_redirect_2.html16
-rw-r--r--docshell/test/navigation/file_session_history_on_redirect_2.html^headers^1
-rw-r--r--docshell/test/navigation/file_sessionhistory_iframe_removal.html37
-rw-r--r--docshell/test/navigation/file_shiftReload_and_pushState.html28
-rw-r--r--docshell/test/navigation/file_ship_beforeunload_fired.html37
-rw-r--r--docshell/test/navigation/file_static_and_dynamic_1.html31
-rw-r--r--docshell/test/navigation/file_tell_opener.html8
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_frame_1.html27
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_frame_2.html8
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a.html6
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a_nav.html6
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_b.html15
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_base.html6
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_nav.html6
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_subframe.html15
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_subframe_nav.html21
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_subframe_same_origin_nav.html20
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_window_open.html6
-rw-r--r--docshell/test/navigation/frame0.html3
-rw-r--r--docshell/test/navigation/frame1.html3
-rw-r--r--docshell/test/navigation/frame2.html3
-rw-r--r--docshell/test/navigation/frame3.html3
-rw-r--r--docshell/test/navigation/frame_1_out_of_6.html6
-rw-r--r--docshell/test/navigation/frame_2_out_of_6.html6
-rw-r--r--docshell/test/navigation/frame_3_out_of_6.html6
-rw-r--r--docshell/test/navigation/frame_4_out_of_6.html6
-rw-r--r--docshell/test/navigation/frame_5_out_of_6.html6
-rw-r--r--docshell/test/navigation/frame_6_out_of_6.html6
-rw-r--r--docshell/test/navigation/frame_load_as_example_com.html6
-rw-r--r--docshell/test/navigation/frame_load_as_example_org.html6
-rw-r--r--docshell/test/navigation/frame_load_as_host1.html6
-rw-r--r--docshell/test/navigation/frame_load_as_host2.html6
-rw-r--r--docshell/test/navigation/frame_load_as_host3.html6
-rw-r--r--docshell/test/navigation/frame_recursive.html6
-rw-r--r--docshell/test/navigation/goback.html5
-rw-r--r--docshell/test/navigation/iframe.html9
-rw-r--r--docshell/test/navigation/iframe_slow_onload.html5
-rw-r--r--docshell/test/navigation/iframe_slow_onload_inner.html19
-rw-r--r--docshell/test/navigation/iframe_static.html8
-rw-r--r--docshell/test/navigation/mochitest.ini255
-rw-r--r--docshell/test/navigation/navigate.html38
-rw-r--r--docshell/test/navigation/navigation_target_popup_url.html1
-rw-r--r--docshell/test/navigation/navigation_target_url.html1
-rw-r--r--docshell/test/navigation/object_recursive_load.html6
-rw-r--r--docshell/test/navigation/open.html10
-rw-r--r--docshell/test/navigation/parent.html14
-rw-r--r--docshell/test/navigation/redbox_bug430723.html6
-rw-r--r--docshell/test/navigation/redirect_handlers.sjs29
-rw-r--r--docshell/test/navigation/redirect_to_blank.sjs6
-rw-r--r--docshell/test/navigation/slow.sjs16
-rw-r--r--docshell/test/navigation/test_aboutblank_change_process.html46
-rw-r--r--docshell/test/navigation/test_beforeunload_and_bfcache.html97
-rw-r--r--docshell/test/navigation/test_blockBFCache.html294
-rw-r--r--docshell/test/navigation/test_bug1300461.html70
-rw-r--r--docshell/test/navigation/test_bug1326251.html47
-rw-r--r--docshell/test/navigation/test_bug1364364.html65
-rw-r--r--docshell/test/navigation/test_bug1375833.html131
-rw-r--r--docshell/test/navigation/test_bug1379762.html67
-rw-r--r--docshell/test/navigation/test_bug13871.html85
-rw-r--r--docshell/test/navigation/test_bug145971.html29
-rw-r--r--docshell/test/navigation/test_bug1536471.html75
-rw-r--r--docshell/test/navigation/test_bug1583110.html36
-rw-r--r--docshell/test/navigation/test_bug1609475.html35
-rw-r--r--docshell/test/navigation/test_bug1699721.html110
-rw-r--r--docshell/test/navigation/test_bug1706090.html49
-rw-r--r--docshell/test/navigation/test_bug1745638.html40
-rw-r--r--docshell/test/navigation/test_bug1747019.html48
-rw-r--r--docshell/test/navigation/test_bug1750973.html20
-rw-r--r--docshell/test/navigation/test_bug1758664.html21
-rw-r--r--docshell/test/navigation/test_bug270414.html103
-rw-r--r--docshell/test/navigation/test_bug278916.html37
-rw-r--r--docshell/test/navigation/test_bug279495.html44
-rw-r--r--docshell/test/navigation/test_bug344861.html35
-rw-r--r--docshell/test/navigation/test_bug386782.html122
-rw-r--r--docshell/test/navigation/test_bug430624.html57
-rw-r--r--docshell/test/navigation/test_bug430723.html124
-rw-r--r--docshell/test/navigation/test_child.html47
-rw-r--r--docshell/test/navigation/test_contentpolicy_block_window.html98
-rw-r--r--docshell/test/navigation/test_docshell_gotoindex.html29
-rw-r--r--docshell/test/navigation/test_dynamic_frame_forward_back.html35
-rw-r--r--docshell/test/navigation/test_evict_from_bfcache.html63
-rw-r--r--docshell/test/navigation/test_fragment_handling_during_load.html35
-rw-r--r--docshell/test/navigation/test_grandchild.html47
-rw-r--r--docshell/test/navigation/test_load_history_entry.html196
-rw-r--r--docshell/test/navigation/test_meta_refresh.html42
-rw-r--r--docshell/test/navigation/test_navigation_type.html47
-rw-r--r--docshell/test/navigation/test_nested_frames.html35
-rw-r--r--docshell/test/navigation/test_new_shentry_during_history_navigation.html90
-rw-r--r--docshell/test/navigation/test_not-opener.html56
-rw-r--r--docshell/test/navigation/test_online_offline_bfcache.html101
-rw-r--r--docshell/test/navigation/test_open_javascript_noopener.html44
-rw-r--r--docshell/test/navigation/test_opener.html56
-rw-r--r--docshell/test/navigation/test_performance_navigation.html41
-rw-r--r--docshell/test/navigation/test_popup-navigates-children.html69
-rw-r--r--docshell/test/navigation/test_rate_limit_location_change.html100
-rw-r--r--docshell/test/navigation/test_recursive_frames.html167
-rw-r--r--docshell/test/navigation/test_reload.html42
-rw-r--r--docshell/test/navigation/test_reload_large_postdata.html61
-rw-r--r--docshell/test/navigation/test_reload_nonbfcached_srcdoc.html40
-rw-r--r--docshell/test/navigation/test_reserved.html92
-rw-r--r--docshell/test/navigation/test_same_url.html56
-rw-r--r--docshell/test/navigation/test_scrollRestoration.html214
-rw-r--r--docshell/test/navigation/test_session_history_entry_cleanup.html35
-rw-r--r--docshell/test/navigation/test_session_history_on_redirect.html92
-rw-r--r--docshell/test/navigation/test_sessionhistory.html48
-rw-r--r--docshell/test/navigation/test_sessionhistory_document_write.html34
-rw-r--r--docshell/test/navigation/test_sessionhistory_iframe_removal.html33
-rw-r--r--docshell/test/navigation/test_shiftReload_and_pushState.html35
-rw-r--r--docshell/test/navigation/test_ship_beforeunload_fired.html63
-rw-r--r--docshell/test/navigation/test_ship_beforeunload_fired_2.html65
-rw-r--r--docshell/test/navigation/test_ship_beforeunload_fired_3.html65
-rw-r--r--docshell/test/navigation/test_sibling-matching-parent.html46
-rw-r--r--docshell/test/navigation/test_sibling-off-domain.html46
-rw-r--r--docshell/test/navigation/test_state_size.html32
-rw-r--r--docshell/test/navigation/test_static_and_dynamic.html36
-rw-r--r--docshell/test/navigation/test_triggeringprincipal_frame_nav.html74
-rw-r--r--docshell/test/navigation/test_triggeringprincipal_frame_same_origin_nav.html63
-rw-r--r--docshell/test/navigation/test_triggeringprincipal_iframe_iframe_window_open.html87
-rw-r--r--docshell/test/navigation/test_triggeringprincipal_parent_iframe_window_open.html70
-rw-r--r--docshell/test/navigation/test_triggeringprincipal_window_open.html79
203 files changed, 8443 insertions, 0 deletions
diff --git a/docshell/test/navigation/NavigationUtils.js b/docshell/test/navigation/NavigationUtils.js
new file mode 100644
index 0000000000..c4b52dc62f
--- /dev/null
+++ b/docshell/test/navigation/NavigationUtils.js
@@ -0,0 +1,203 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// /////////////////////////////////////////////////////////////////////////
+//
+// Utilities for navigation tests
+//
+// /////////////////////////////////////////////////////////////////////////
+
+var body = "This frame was navigated.";
+var target_url = "navigation_target_url.html";
+
+var popup_body = "This is a popup";
+var target_popup_url = "navigation_target_popup_url.html";
+
+// /////////////////////////////////////////////////////////////////////////
+// Functions that navigate frames
+// /////////////////////////////////////////////////////////////////////////
+
+function navigateByLocation(wnd) {
+ try {
+ wnd.location = target_url;
+ } catch (ex) {
+ // We need to keep our finished frames count consistent.
+ // Oddly, this ends up simulating the behavior of IE7.
+ window.open(target_url, "_blank", "width=10,height=10");
+ }
+}
+
+function navigateByOpen(name) {
+ window.open(target_url, name, "width=10,height=10");
+}
+
+function navigateByForm(name) {
+ var form = document.createElement("form");
+ form.action = target_url;
+ form.method = "POST";
+ form.target = name;
+ document.body.appendChild(form);
+ form.submit();
+}
+
+var hyperlink_count = 0;
+
+function navigateByHyperlink(name) {
+ var link = document.createElement("a");
+ link.href = target_url;
+ link.target = name;
+ link.id = "navigation_hyperlink_" + hyperlink_count++;
+ document.body.appendChild(link);
+ sendMouseEvent({ type: "click" }, link.id);
+}
+
+// /////////////////////////////////////////////////////////////////////////
+// Functions that call into Mochitest framework
+// /////////////////////////////////////////////////////////////////////////
+
+async function isNavigated(wnd, message) {
+ var result = null;
+ try {
+ result = await SpecialPowers.spawn(wnd, [], () =>
+ this.content.document.body.innerHTML.trim()
+ );
+ } catch (ex) {
+ result = ex;
+ }
+ is(result, body, message);
+}
+
+function isBlank(wnd, message) {
+ var result = null;
+ try {
+ result = wnd.document.body.innerHTML.trim();
+ } catch (ex) {
+ result = ex;
+ }
+ is(result, "This is a blank document.", message);
+}
+
+function isAccessible(wnd, message) {
+ try {
+ wnd.document.body.innerHTML;
+ ok(true, message);
+ } catch (ex) {
+ ok(false, message);
+ }
+}
+
+function isInaccessible(wnd, message) {
+ try {
+ wnd.document.body.innerHTML;
+ ok(false, message);
+ } catch (ex) {
+ ok(true, message);
+ }
+}
+
+function delay(msec) {
+ return new Promise(resolve => setTimeout(resolve, msec));
+}
+
+// /////////////////////////////////////////////////////////////////////////
+// Functions that uses SpecialPowers.spawn
+// /////////////////////////////////////////////////////////////////////////
+
+async function waitForFinishedFrames(numFrames) {
+ SimpleTest.requestFlakyTimeout("Polling");
+
+ var finishedWindows = new Set();
+
+ async function searchForFinishedFrames(win) {
+ try {
+ let { href, bodyText, readyState } = await SpecialPowers.spawn(
+ win,
+ [],
+ () => {
+ return {
+ href: this.content.location.href,
+ bodyText:
+ this.content.document.body &&
+ this.content.document.body.textContent.trim(),
+ readyState: this.content.document.readyState,
+ };
+ }
+ );
+
+ if (
+ (href.endsWith(target_url) || href.endsWith(target_popup_url)) &&
+ (bodyText == body || bodyText == popup_body) &&
+ readyState == "complete"
+ ) {
+ finishedWindows.add(SpecialPowers.getBrowsingContextID(win));
+ }
+ } catch (e) {
+ // This may throw if a frame is not fully initialized, in which
+ // case we'll handle it in a later iteration.
+ }
+
+ for (let i = 0; i < win.frames.length; i++) {
+ await searchForFinishedFrames(win.frames[i]);
+ }
+ }
+
+ while (finishedWindows.size < numFrames) {
+ await delay(500);
+
+ for (let win of SpecialPowers.getGroupTopLevelWindows(window)) {
+ win = SpecialPowers.unwrap(win);
+ await searchForFinishedFrames(win);
+ }
+ }
+
+ if (finishedWindows.size > numFrames) {
+ throw new Error("Too many frames loaded.");
+ }
+}
+
+async function getFramesByName(name) {
+ let results = [];
+ for (let win of SpecialPowers.getGroupTopLevelWindows(window)) {
+ win = SpecialPowers.unwrap(win);
+ if (
+ (await SpecialPowers.spawn(win, [], () => this.content.name)) === name
+ ) {
+ results.push(win);
+ }
+ }
+
+ return results;
+}
+
+async function cleanupWindows() {
+ for (let win of SpecialPowers.getGroupTopLevelWindows(window)) {
+ win = SpecialPowers.unwrap(win);
+ if (win.closed) {
+ continue;
+ }
+
+ let href = "";
+ try {
+ href = await SpecialPowers.spawn(
+ win,
+ [],
+ () =>
+ this.content && this.content.location && this.content.location.href
+ );
+ } catch (error) {
+ // SpecialPowers.spawn(win, ...) throws if win is closed. We did
+ // our best to not call it on a closed window, but races happen.
+ if (!win.closed) {
+ throw error;
+ }
+ }
+
+ if (
+ href &&
+ (href.endsWith(target_url) || href.endsWith(target_popup_url))
+ ) {
+ win.close();
+ }
+ }
+}
diff --git a/docshell/test/navigation/blank.html b/docshell/test/navigation/blank.html
new file mode 100644
index 0000000000..5360333f1d
--- /dev/null
+++ b/docshell/test/navigation/blank.html
@@ -0,0 +1 @@
+<html><body>This is a blank document.</body></html> \ No newline at end of file
diff --git a/docshell/test/navigation/bluebox_bug430723.html b/docshell/test/navigation/bluebox_bug430723.html
new file mode 100644
index 0000000000..5dcc533562
--- /dev/null
+++ b/docshell/test/navigation/bluebox_bug430723.html
@@ -0,0 +1,6 @@
+<html><head>
+<script> window.addEventListener("pageshow", function() { opener.nextTest(); }); </script>
+</head><body>
+<div style="position:absolute; left:0px; top:0px; width:50%; height:150%; background-color:blue">
+<p>This is a very tall blue box.</p>
+</div></body></html>
diff --git a/docshell/test/navigation/browser.ini b/docshell/test/navigation/browser.ini
new file mode 100644
index 0000000000..baca6b401c
--- /dev/null
+++ b/docshell/test/navigation/browser.ini
@@ -0,0 +1,23 @@
+[DEFAULT]
+support-files =
+ bug343515_pg1.html
+ bug343515_pg2.html
+ bug343515_pg3.html
+ bug343515_pg3_1.html
+ bug343515_pg3_1_1.html
+ bug343515_pg3_2.html
+ blank.html
+ redirect_to_blank.sjs
+
+[browser_bug1757458.js]
+[browser_test_bfcache_eviction.js]
+[browser_bug343515.js]
+[browser_test-content-chromeflags.js]
+tags = openwindow
+[browser_ghistorymaxsize_is_0.js]
+[browser_test_shentry_wireframe.js]
+skip-if =
+ !sessionHistoryInParent
+ os == "win" && os_version == "6.1" # Skip on Azure - frequent failure
+[browser_test_simultaneous_normal_and_history_loads.js]
+skip-if = !sessionHistoryInParent # The test is for the new session history
diff --git a/docshell/test/navigation/browser_bug1757458.js b/docshell/test/navigation/browser_bug1757458.js
new file mode 100644
index 0000000000..cc9504ac1c
--- /dev/null
+++ b/docshell/test/navigation/browser_bug1757458.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function () {
+ let testPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "view-source:http://example.com"
+ ) + "redirect_to_blank.sjs";
+
+ let testPage2 = "data:text/html,<div>Second page</div>";
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: testPage },
+ async function (browser) {
+ await ContentTask.spawn(browser, [], async () => {
+ Assert.ok(
+ content.document.getElementById("viewsource").localName == "body",
+ "view-source document's body should have id='viewsource'."
+ );
+ content.document
+ .getElementById("viewsource")
+ .setAttribute("onunload", "/* disable bfcache*/");
+ });
+
+ BrowserTestUtils.loadURIString(browser, testPage2);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ let pageShownPromise = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "pageshow",
+ true
+ );
+ browser.browsingContext.goBack();
+ await pageShownPromise;
+
+ await ContentTask.spawn(browser, [], async () => {
+ Assert.ok(
+ content.document.getElementById("viewsource").localName == "body",
+ "view-source document's body should have id='viewsource'."
+ );
+ });
+ }
+ );
+});
diff --git a/docshell/test/navigation/browser_bug343515.js b/docshell/test/navigation/browser_bug343515.js
new file mode 100644
index 0000000000..722b1bc018
--- /dev/null
+++ b/docshell/test/navigation/browser_bug343515.js
@@ -0,0 +1,276 @@
+// Test for bug 343515 - Need API for tabbrowsers to tell docshells they're visible/hidden
+
+// Globals
+var testPath = "http://mochi.test:8888/browser/docshell/test/navigation/";
+var ctx = {};
+
+add_task(async function () {
+ // Step 1.
+
+ // Get a handle on the initial tab
+ ctx.tab0 = gBrowser.selectedTab;
+ ctx.tab0Browser = gBrowser.getBrowserForTab(ctx.tab0);
+
+ await BrowserTestUtils.waitForCondition(
+ () => ctx.tab0Browser.docShellIsActive,
+ "Timed out waiting for initial tab to be active."
+ );
+
+ // Open a New Tab
+ ctx.tab1 = BrowserTestUtils.addTab(gBrowser, testPath + "bug343515_pg1.html");
+ ctx.tab1Browser = gBrowser.getBrowserForTab(ctx.tab1);
+ await BrowserTestUtils.browserLoaded(ctx.tab1Browser);
+
+ // Step 2.
+ is(
+ testPath + "bug343515_pg1.html",
+ ctx.tab1Browser.currentURI.spec,
+ "Got expected tab 1 url in step 2"
+ );
+
+ // Our current tab should still be active
+ ok(ctx.tab0Browser.docShellIsActive, "Tab 0 should still be active");
+ ok(!ctx.tab1Browser.docShellIsActive, "Tab 1 should not be active");
+
+ // Switch to tab 1
+ await BrowserTestUtils.switchTab(gBrowser, ctx.tab1);
+
+ // Tab 1 should now be active
+ ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
+ ok(ctx.tab1Browser.docShellIsActive, "Tab 1 should be active");
+
+ // Open another tab
+ ctx.tab2 = BrowserTestUtils.addTab(gBrowser, testPath + "bug343515_pg2.html");
+ ctx.tab2Browser = gBrowser.getBrowserForTab(ctx.tab2);
+
+ await BrowserTestUtils.browserLoaded(ctx.tab2Browser);
+
+ // Step 3.
+ is(
+ testPath + "bug343515_pg2.html",
+ ctx.tab2Browser.currentURI.spec,
+ "Got expected tab 2 url in step 3"
+ );
+
+ // Tab 0 should be inactive, Tab 1 should be active
+ ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
+ ok(ctx.tab1Browser.docShellIsActive, "Tab 1 should be active");
+
+ // Tab 2's window _and_ its iframes should be inactive
+ ok(!ctx.tab2Browser.docShellIsActive, "Tab 2 should be inactive");
+
+ await SpecialPowers.spawn(ctx.tab2Browser, [], async function () {
+ Assert.equal(content.frames.length, 2, "Tab 2 should have 2 iframes");
+ for (var i = 0; i < content.frames.length; i++) {
+ info("step 3, frame " + i + " info: " + content.frames[i].location);
+ let bc = content.frames[i].browsingContext;
+ Assert.ok(!bc.isActive, `Tab2 iframe ${i} should be inactive`);
+ }
+ });
+
+ // Navigate tab 2 to a different page
+ BrowserTestUtils.loadURIString(
+ ctx.tab2Browser,
+ testPath + "bug343515_pg3.html"
+ );
+
+ await BrowserTestUtils.browserLoaded(ctx.tab2Browser);
+
+ // Step 4.
+
+ async function checkTab2Active(outerExpected) {
+ await SpecialPowers.spawn(
+ ctx.tab2Browser,
+ [outerExpected],
+ async function (expected) {
+ function isActive(aWindow) {
+ var docshell = aWindow.docShell;
+ info(`checking ${docshell.browsingContext.id}`);
+ return docshell.browsingContext.isActive;
+ }
+
+ let active = expected ? "active" : "inactive";
+ Assert.equal(content.frames.length, 2, "Tab 2 should have 2 iframes");
+ for (var i = 0; i < content.frames.length; i++) {
+ info("step 4, frame " + i + " info: " + content.frames[i].location);
+ }
+ Assert.equal(
+ content.frames[0].frames.length,
+ 1,
+ "Tab 2 iframe 0 should have 1 iframes"
+ );
+ Assert.equal(
+ isActive(content.frames[0]),
+ expected,
+ `Tab2 iframe 0 should be ${active}`
+ );
+ Assert.equal(
+ isActive(content.frames[0].frames[0]),
+ expected,
+ `Tab2 iframe 0 subiframe 0 should be ${active}`
+ );
+ Assert.equal(
+ isActive(content.frames[1]),
+ expected,
+ `Tab2 iframe 1 should be ${active}`
+ );
+ }
+ );
+ }
+
+ is(
+ testPath + "bug343515_pg3.html",
+ ctx.tab2Browser.currentURI.spec,
+ "Got expected tab 2 url in step 4"
+ );
+
+ // Tab 0 should be inactive, Tab 1 should be active
+ ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
+ ok(ctx.tab1Browser.docShellIsActive, "Tab 1 should be active");
+
+ // Tab2 and all descendants should be inactive
+ await checkTab2Active(false);
+
+ // Switch to Tab 2
+ await BrowserTestUtils.switchTab(gBrowser, ctx.tab2);
+
+ // Check everything
+ ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
+ ok(!ctx.tab1Browser.docShellIsActive, "Tab 1 should be inactive");
+ ok(ctx.tab2Browser.docShellIsActive, "Tab 2 should be active");
+
+ await checkTab2Active(true);
+
+ // Go back
+ let backDone = BrowserTestUtils.waitForLocationChange(
+ gBrowser,
+ testPath + "bug343515_pg2.html"
+ );
+ ctx.tab2Browser.goBack();
+ await backDone;
+
+ // Step 5.
+
+ // Check everything
+ ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
+ ok(!ctx.tab1Browser.docShellIsActive, "Tab 1 should be inactive");
+ ok(ctx.tab2Browser.docShellIsActive, "Tab 2 should be active");
+ is(
+ testPath + "bug343515_pg2.html",
+ ctx.tab2Browser.currentURI.spec,
+ "Got expected tab 2 url in step 5"
+ );
+
+ await SpecialPowers.spawn(ctx.tab2Browser, [], async function () {
+ for (var i = 0; i < content.frames.length; i++) {
+ let bc = content.frames[i].browsingContext;
+ Assert.ok(bc.isActive, `Tab2 iframe ${i} should be active`);
+ }
+ });
+
+ // Switch to tab 1
+ await BrowserTestUtils.switchTab(gBrowser, ctx.tab1);
+
+ // Navigate to page 3
+ BrowserTestUtils.loadURIString(
+ ctx.tab1Browser,
+ testPath + "bug343515_pg3.html"
+ );
+
+ await BrowserTestUtils.browserLoaded(ctx.tab1Browser);
+
+ // Step 6.
+
+ // Check everything
+ ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
+ ok(ctx.tab1Browser.docShellIsActive, "Tab 1 should be active");
+ is(
+ testPath + "bug343515_pg3.html",
+ ctx.tab1Browser.currentURI.spec,
+ "Got expected tab 1 url in step 6"
+ );
+
+ await SpecialPowers.spawn(ctx.tab1Browser, [], async function () {
+ function isActive(aWindow) {
+ var docshell = aWindow.docShell;
+ info(`checking ${docshell.browsingContext.id}`);
+ return docshell.browsingContext.isActive;
+ }
+
+ Assert.ok(isActive(content.frames[0]), "Tab1 iframe 0 should be active");
+ Assert.ok(
+ isActive(content.frames[0].frames[0]),
+ "Tab1 iframe 0 subiframe 0 should be active"
+ );
+ Assert.ok(isActive(content.frames[1]), "Tab1 iframe 1 should be active");
+ });
+
+ ok(!ctx.tab2Browser.docShellIsActive, "Tab 2 should be inactive");
+
+ await SpecialPowers.spawn(ctx.tab2Browser, [], async function () {
+ for (var i = 0; i < content.frames.length; i++) {
+ let bc = content.frames[i].browsingContext;
+ Assert.ok(!bc.isActive, `Tab2 iframe ${i} should be inactive`);
+ }
+ });
+
+ // Go forward on tab 2
+ let forwardDone = BrowserTestUtils.waitForLocationChange(
+ gBrowser,
+ testPath + "bug343515_pg3.html"
+ );
+ ctx.tab2Browser.goForward();
+ await forwardDone;
+
+ // Step 7.
+
+ async function checkBrowser(browser, outerTabNum, outerActive) {
+ let data = { tabNum: outerTabNum, active: outerActive };
+ await SpecialPowers.spawn(
+ browser,
+ [data],
+ async function ({ tabNum, active }) {
+ function isActive(aWindow) {
+ var docshell = aWindow.docShell;
+ info(`checking ${docshell.browsingContext.id}`);
+ return docshell.browsingContext.isActive;
+ }
+
+ let activestr = active ? "active" : "inactive";
+ Assert.equal(
+ isActive(content.frames[0]),
+ active,
+ `Tab${tabNum} iframe 0 should be ${activestr}`
+ );
+ Assert.equal(
+ isActive(content.frames[0].frames[0]),
+ active,
+ `Tab${tabNum} iframe 0 subiframe 0 should be ${activestr}`
+ );
+ Assert.equal(
+ isActive(content.frames[1]),
+ active,
+ `Tab${tabNum} iframe 1 should be ${activestr}`
+ );
+ }
+ );
+ }
+
+ // Check everything
+ ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
+ ok(ctx.tab1Browser.docShellIsActive, "Tab 1 should be active");
+ is(
+ testPath + "bug343515_pg3.html",
+ ctx.tab2Browser.currentURI.spec,
+ "Got expected tab 2 url in step 7"
+ );
+
+ await checkBrowser(ctx.tab1Browser, 1, true);
+
+ ok(!ctx.tab2Browser.docShellIsActive, "Tab 2 should be inactive");
+ await checkBrowser(ctx.tab2Browser, 2, false);
+
+ // Close the tabs we made
+ BrowserTestUtils.removeTab(ctx.tab1);
+ BrowserTestUtils.removeTab(ctx.tab2);
+});
diff --git a/docshell/test/navigation/browser_ghistorymaxsize_is_0.js b/docshell/test/navigation/browser_ghistorymaxsize_is_0.js
new file mode 100644
index 0000000000..43c36bbaf2
--- /dev/null
+++ b/docshell/test/navigation/browser_ghistorymaxsize_is_0.js
@@ -0,0 +1,82 @@
+add_task(async function () {
+ // The urls don't really matter as long as they are of the same origin
+ var URL =
+ "http://mochi.test:8888/browser/docshell/test/navigation/bug343515_pg1.html";
+ var URL2 =
+ "http://mochi.test:8888/browser/docshell/test/navigation/bug343515_pg3_1.html";
+
+ // We want to test a specific code path that leads to this call
+ // https://searchfox.org/mozilla-central/rev/e7c61f4a68b974d5fecd216dc7407b631a24eb8f/docshell/base/nsDocShell.cpp#10795
+ // when gHistoryMaxSize is 0 and mIndex and mRequestedIndex are -1
+
+ // 1. Navigate to URL
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: URL },
+ async function (browser) {
+ // At this point, we haven't set gHistoryMaxSize to 0, and it is still 50 (default value).
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ let sh = browser.browsingContext.sessionHistory;
+ is(
+ sh.count,
+ 1,
+ "We should have entry in session history because we haven't changed gHistoryMaxSize to be 0 yet"
+ );
+ is(
+ sh.index,
+ 0,
+ "Shistory's current index should be 0 because we haven't purged history yet"
+ );
+ } else {
+ await ContentTask.spawn(browser, null, () => {
+ var sh = content.window.docShell.QueryInterface(Ci.nsIWebNavigation)
+ .sessionHistory.legacySHistory;
+ is(
+ sh.count,
+ 1,
+ "We should have entry in session history because we haven't changed gHistoryMaxSize to be 0 yet"
+ );
+ is(
+ sh.index,
+ 0,
+ "Shistory's current index should be 0 because we haven't purged history yet"
+ );
+ });
+ }
+
+ var loadPromise = BrowserTestUtils.browserLoaded(browser, false, URL2);
+ // If we set the pref at the beginning of this page, then when we launch a child process
+ // to navigate to URL in Step 1, because of
+ // https://searchfox.org/mozilla-central/rev/e7c61f4a68b974d5fecd216dc7407b631a24eb8f/docshell/shistory/nsSHistory.cpp#308-312
+ // this pref will be set to the default value (currently 50). Setting this pref after the child process launches
+ // is a robust way to make sure it stays 0
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.sessionhistory.max_entries", 0]],
+ });
+ // 2. Navigate to URL2
+ // We are navigating to a page with the same origin so that we will stay in the same process
+ BrowserTestUtils.loadURIString(browser, URL2);
+ await loadPromise;
+
+ // 3. Reload the browser with specific flags so that we end up here
+ // https://searchfox.org/mozilla-central/rev/e7c61f4a68b974d5fecd216dc7407b631a24eb8f/docshell/base/nsDocShell.cpp#10795
+ var promise = BrowserTestUtils.browserLoaded(browser);
+ browser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
+ await promise;
+
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ let sh = browser.browsingContext.sessionHistory;
+ is(sh.count, 0, "We should not save any entries in session history");
+ is(sh.index, -1);
+ is(sh.requestedIndex, -1);
+ } else {
+ await ContentTask.spawn(browser, null, () => {
+ var sh = content.window.docShell.QueryInterface(Ci.nsIWebNavigation)
+ .sessionHistory.legacySHistory;
+ is(sh.count, 0, "We should not save any entries in session history");
+ is(sh.index, -1);
+ is(sh.requestedIndex, -1);
+ });
+ }
+ }
+ );
+});
diff --git a/docshell/test/navigation/browser_test-content-chromeflags.js b/docshell/test/navigation/browser_test-content-chromeflags.js
new file mode 100644
index 0000000000..ab1ae41c0c
--- /dev/null
+++ b/docshell/test/navigation/browser_test-content-chromeflags.js
@@ -0,0 +1,54 @@
+const TEST_PAGE = `data:text/html,<html><body><a href="about:blank" target="_blank">Test</a></body></html>`;
+const { CHROME_ALL, CHROME_REMOTE_WINDOW, CHROME_FISSION_WINDOW } =
+ Ci.nsIWebBrowserChrome;
+
+/**
+ * Tests that when we open new browser windows from content they
+ * get the full browser chrome.
+ */
+add_task(async function () {
+ // Make sure that the window.open call will open a new
+ // window instead of a new tab.
+ await new Promise(resolve => {
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [["browser.link.open_newwindow", 2]],
+ },
+ resolve
+ );
+ });
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async function (browser) {
+ let openedPromise = BrowserTestUtils.waitForNewWindow();
+ BrowserTestUtils.synthesizeMouse("a", 0, 0, {}, browser);
+ let win = await openedPromise;
+
+ let chromeFlags = win.docShell.treeOwner
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIAppWindow).chromeFlags;
+
+ let expected = CHROME_ALL;
+
+ // In the multi-process tab case, the new window will have the
+ // CHROME_REMOTE_WINDOW flag set.
+ if (gMultiProcessBrowser) {
+ expected |= CHROME_REMOTE_WINDOW;
+ }
+
+ // In the multi-process subframe case, the new window will have the
+ // CHROME_FISSION_WINDOW flag set.
+ if (gFissionBrowser) {
+ expected |= CHROME_FISSION_WINDOW;
+ }
+
+ is(chromeFlags, expected, "Window should have opened with all chrome");
+
+ await BrowserTestUtils.closeWindow(win);
+ }
+ );
+});
diff --git a/docshell/test/navigation/browser_test_bfcache_eviction.js b/docshell/test/navigation/browser_test_bfcache_eviction.js
new file mode 100644
index 0000000000..32772fe42c
--- /dev/null
+++ b/docshell/test/navigation/browser_test_bfcache_eviction.js
@@ -0,0 +1,102 @@
+add_task(async function () {
+ // We don't want the number of total viewers to be calculated by the available size
+ // for this test case. Instead, fix the number of viewers.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.sessionhistory.max_total_viewers", 3],
+ ["docshell.shistory.testing.bfevict", true],
+ // If Fission is disabled, the pref is no-op.
+ ["fission.bfcacheInParent", true],
+ ],
+ });
+
+ // 1. Open a tab
+ var testPage =
+ "data:text/html,<html id='html1'><body id='body1'>First tab ever opened</body></html>";
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: testPage },
+ async function (browser) {
+ let testDone = {};
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ // 2. Add a promise that will be resolved when the 'content viewer evicted' event goes off
+ testDone.promise = SpecialPowers.spawn(browser, [], async function () {
+ return new Promise(resolve => {
+ let webNavigation = content.docShell.QueryInterface(
+ Ci.nsIWebNavigation
+ );
+ let { legacySHistory } = webNavigation.sessionHistory;
+ // 3. Register a session history listener to listen for a 'content viewer evicted' event.
+ let historyListener = {
+ OnContentViewerEvicted() {
+ ok(
+ true,
+ "History listener got called after a content viewer was evicted"
+ );
+ legacySHistory.removeSHistoryListener(historyListener);
+ // 6. Resolve the promise when we got our 'content viewer evicted' event
+ resolve();
+ },
+ QueryInterface: ChromeUtils.generateQI([
+ "nsISHistoryListener",
+ "nsISupportsWeakReference",
+ ]),
+ };
+ legacySHistory.addSHistoryListener(historyListener);
+ // Keep the weak shistory listener alive
+ content._testListener = historyListener;
+ });
+ });
+ } else {
+ // 2. Add a promise that will be resolved when the 'content viewer evicted' event goes off
+ testDone.promise = new Promise(resolve => {
+ testDone.resolve = resolve;
+ });
+ let shistory = browser.browsingContext.sessionHistory;
+ // 3. Register a session history listener to listen for a 'content viewer evicted' event.
+ let historyListener = {
+ OnContentViewerEvicted() {
+ ok(
+ true,
+ "History listener got called after a content viewer was evicted"
+ );
+ shistory.removeSHistoryListener(historyListener);
+ delete window._testListener;
+ // 6. Resolve the promise when we got our 'content viewer evicted' event
+ testDone.resolve();
+ },
+ QueryInterface: ChromeUtils.generateQI([
+ "nsISHistoryListener",
+ "nsISupportsWeakReference",
+ ]),
+ };
+ shistory.addSHistoryListener(historyListener);
+ // Keep the weak shistory listener alive
+ window._testListener = historyListener;
+ }
+
+ // 4. Open a second tab
+ testPage = `data:text/html,<html id='html1'><body id='body1'>I am a second tab!</body></html>`;
+ let tab2 = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ testPage
+ );
+
+ // 5. Navigate the first tab to 4 different pages.
+ // We should get 1 content viewer evicted because it will be outside of the range.
+ // If we have the following pages in our session history: P1 P2 P3 P4 P5
+ // and we are currently at P5, then P1 is outside of the range
+ // (it is more than 3 entries away from current entry) and thus will be evicted.
+ for (var i = 0; i < 4; i++) {
+ testPage = `data:text/html,<html id='html1'><body id='body1'>${i}</body></html>`;
+ let pagePromise = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.loadURIString(browser, testPage);
+ await pagePromise;
+ }
+ // 7. Wait for 'content viewer evicted' event to go off
+ await testDone.promise;
+
+ // 8. Close the second tab
+ BrowserTestUtils.removeTab(tab2);
+ }
+ );
+});
diff --git a/docshell/test/navigation/browser_test_shentry_wireframe.js b/docshell/test/navigation/browser_test_shentry_wireframe.js
new file mode 100644
index 0000000000..953a2eb6e2
--- /dev/null
+++ b/docshell/test/navigation/browser_test_shentry_wireframe.js
@@ -0,0 +1,128 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const BUILDER = "http://mochi.test:8888/document-builder.sjs?html=";
+const PAGE_1 = BUILDER + encodeURIComponent(`<html><body>Page 1</body></html>`);
+const PAGE_2 = BUILDER + encodeURIComponent(`<html><body>Page 2</body></html>`);
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.history.collectWireframes", true]],
+ });
+});
+
+/**
+ * Test that capturing wireframes on nsISHEntriy's in the parent process
+ * happens at the right times.
+ */
+add_task(async function () {
+ await BrowserTestUtils.withNewTab(PAGE_1, async browser => {
+ let sh = browser.browsingContext.sessionHistory;
+ Assert.equal(
+ sh.count,
+ 1,
+ "Got the right SessionHistory entry count after initial tab load."
+ );
+ Assert.ok(
+ !sh.getEntryAtIndex(0).wireframe,
+ "No wireframe for the loaded entry after initial tab load."
+ );
+
+ let loaded = BrowserTestUtils.browserLoaded(browser, false, PAGE_2);
+ BrowserTestUtils.loadURIString(browser, PAGE_2);
+ await loaded;
+
+ Assert.equal(
+ sh.count,
+ 2,
+ "Got the right SessionHistory entry count after loading page 2."
+ );
+ Assert.ok(
+ sh.getEntryAtIndex(0).wireframe,
+ "A wireframe was captured for the first entry after loading page 2."
+ );
+ Assert.ok(
+ !sh.getEntryAtIndex(1).wireframe,
+ "No wireframe for the loaded entry after loading page 2."
+ );
+
+ // Now go back
+ loaded = BrowserTestUtils.waitForContentEvent(browser, "pageshow");
+ browser.goBack();
+ await loaded;
+ Assert.ok(
+ sh.getEntryAtIndex(1).wireframe,
+ "A wireframe was captured for the second entry after going back."
+ );
+ Assert.ok(
+ !sh.getEntryAtIndex(0).wireframe,
+ "No wireframe for the loaded entry after going back."
+ );
+
+ // Now forward again
+ loaded = BrowserTestUtils.waitForContentEvent(browser, "pageshow");
+ browser.goForward();
+ await loaded;
+
+ Assert.equal(
+ sh.count,
+ 2,
+ "Got the right SessionHistory entry count after going forward again."
+ );
+ Assert.ok(
+ sh.getEntryAtIndex(0).wireframe,
+ "A wireframe was captured for the first entry after going forward again."
+ );
+ Assert.ok(
+ !sh.getEntryAtIndex(1).wireframe,
+ "No wireframe for the loaded entry after going forward again."
+ );
+
+ // And using pushState
+ await SpecialPowers.spawn(browser, [], async () => {
+ content.history.pushState({}, "", "nothing-1.html");
+ content.history.pushState({}, "", "nothing-2.html");
+ });
+
+ Assert.equal(
+ sh.count,
+ 4,
+ "Got the right SessionHistory entry count after using pushState."
+ );
+ Assert.ok(
+ sh.getEntryAtIndex(0).wireframe,
+ "A wireframe was captured for the first entry after using pushState."
+ );
+ Assert.ok(
+ sh.getEntryAtIndex(1).wireframe,
+ "A wireframe was captured for the second entry after using pushState."
+ );
+ Assert.ok(
+ sh.getEntryAtIndex(2).wireframe,
+ "A wireframe was captured for the third entry after using pushState."
+ );
+ Assert.ok(
+ !sh.getEntryAtIndex(3).wireframe,
+ "No wireframe for the loaded entry after using pushState."
+ );
+
+ // Now check that wireframes can be written to in case we're restoring
+ // an nsISHEntry from serialization.
+ let wireframe = sh.getEntryAtIndex(2).wireframe;
+ sh.getEntryAtIndex(2).wireframe = null;
+ Assert.equal(
+ sh.getEntryAtIndex(2).wireframe,
+ null,
+ "Successfully cleared wireframe."
+ );
+
+ sh.getEntryAtIndex(3).wireframe = wireframe;
+ Assert.deepEqual(
+ sh.getEntryAtIndex(3).wireframe,
+ wireframe,
+ "Successfully wrote a wireframe to an nsISHEntry."
+ );
+ });
+});
diff --git a/docshell/test/navigation/browser_test_simultaneous_normal_and_history_loads.js b/docshell/test/navigation/browser_test_simultaneous_normal_and_history_loads.js
new file mode 100644
index 0000000000..a400d15466
--- /dev/null
+++ b/docshell/test/navigation/browser_test_simultaneous_normal_and_history_loads.js
@@ -0,0 +1,57 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_normal_and_history_loads() {
+ // This test is for the case when session history lives in the parent process.
+ // BFCache is disabled to get more asynchronousness.
+ await SpecialPowers.pushPrefEnv({
+ set: [["fission.bfcacheInParent", false]],
+ });
+
+ let testPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+ ) + "blank.html";
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: testPage },
+ async function (browser) {
+ for (let i = 0; i < 2; ++i) {
+ BrowserTestUtils.loadURIString(browser, testPage + "?" + i);
+ await BrowserTestUtils.browserLoaded(browser);
+ }
+
+ let sh = browser.browsingContext.sessionHistory;
+ is(sh.count, 3, "Should have 3 entries in the session history.");
+ is(sh.index, 2, "index should be 2");
+ is(sh.requestedIndex, -1, "requestedIndex should be -1");
+
+ // The following part is racy by definition. It is testing that
+ // eventually requestedIndex should become -1 again.
+ browser.browsingContext.goToIndex(1);
+ let historyLoad = BrowserTestUtils.browserLoaded(browser);
+ /* eslint-disable mozilla/no-arbitrary-setTimeout */
+ await new Promise(r => {
+ setTimeout(r, 10);
+ });
+ browser.browsingContext.loadURI(
+ Services.io.newURI(testPage + "?newload"),
+ {
+ triggeringPrincipal: browser.nodePrincipal,
+ }
+ );
+ let newLoad = BrowserTestUtils.browserLoaded(browser);
+ // Note, the loads are racy.
+ await historyLoad;
+ await newLoad;
+ is(sh.requestedIndex, -1, "requestedIndex should be -1");
+
+ browser.browsingContext.goBack();
+ await BrowserTestUtils.browserLoaded(browser);
+ is(sh.requestedIndex, -1, "requestedIndex should be -1");
+ }
+ );
+});
diff --git a/docshell/test/navigation/bug343515_pg1.html b/docshell/test/navigation/bug343515_pg1.html
new file mode 100644
index 0000000000..a8337c7f70
--- /dev/null
+++ b/docshell/test/navigation/bug343515_pg1.html
@@ -0,0 +1,5 @@
+<html>
+ <head><meta charset="UTF-8"/></head>
+ <body>Page 1
+ </body>
+</html>
diff --git a/docshell/test/navigation/bug343515_pg2.html b/docshell/test/navigation/bug343515_pg2.html
new file mode 100644
index 0000000000..c5f5665de5
--- /dev/null
+++ b/docshell/test/navigation/bug343515_pg2.html
@@ -0,0 +1,7 @@
+<html>
+ <head><meta charset="UTF-8"/></head>
+ <body>Page 2
+ <iframe src="data:text/html;charset=UTF8,<html><head></head><body>pg2 iframe 0</body></html>"></iframe>
+ <iframe src="data:text/html;charset=UTF8,<html><head></head><body>pg2 iframe 1</body></html>"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/bug343515_pg3.html b/docshell/test/navigation/bug343515_pg3.html
new file mode 100644
index 0000000000..fdc79fbf7a
--- /dev/null
+++ b/docshell/test/navigation/bug343515_pg3.html
@@ -0,0 +1,7 @@
+<html>
+ <head><meta charset="UTF-8"/></head>
+ <body>Page 3
+ <iframe src="bug343515_pg3_1.html"></iframe>
+ <iframe src="bug343515_pg3_2.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/bug343515_pg3_1.html b/docshell/test/navigation/bug343515_pg3_1.html
new file mode 100644
index 0000000000..254164c9f0
--- /dev/null
+++ b/docshell/test/navigation/bug343515_pg3_1.html
@@ -0,0 +1,6 @@
+<html>
+ <head><meta charset="UTF-8"/></head>
+ <body>pg3 - iframe 0
+ <iframe src="bug343515_pg3_1_1.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/bug343515_pg3_1_1.html b/docshell/test/navigation/bug343515_pg3_1_1.html
new file mode 100644
index 0000000000..be05b74888
--- /dev/null
+++ b/docshell/test/navigation/bug343515_pg3_1_1.html
@@ -0,0 +1 @@
+<html><head><meta charset="UTF-8"/></head><body>How far does the rabbit hole go?</body></html>
diff --git a/docshell/test/navigation/bug343515_pg3_2.html b/docshell/test/navigation/bug343515_pg3_2.html
new file mode 100644
index 0000000000..7655eb526d
--- /dev/null
+++ b/docshell/test/navigation/bug343515_pg3_2.html
@@ -0,0 +1 @@
+<html><head><meta charset="UTF-8"/></head><body>pg3 iframe 1</body></html>
diff --git a/docshell/test/navigation/cache_control_max_age_3600.sjs b/docshell/test/navigation/cache_control_max_age_3600.sjs
new file mode 100644
index 0000000000..49b6439c9f
--- /dev/null
+++ b/docshell/test/navigation/cache_control_max_age_3600.sjs
@@ -0,0 +1,20 @@
+function handleRequest(request, response) {
+ let query = request.queryString;
+ let action =
+ query == "initial"
+ ? "cache_control_max_age_3600.sjs?second"
+ : "goback.html";
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Cache-Control", "max-age=3600");
+ response.write(
+ "<html><head><script>window.blockBFCache = new RTCPeerConnection();</script></head>" +
+ '<body onload=\'opener.postMessage("loaded", "*")\'>' +
+ "<div id='content'>" +
+ new Date().getTime() +
+ "</div>" +
+ "<form action='" +
+ action +
+ "' method='POST'></form>" +
+ "</body></html>"
+ );
+}
diff --git a/docshell/test/navigation/file_beforeunload_and_bfcache.html b/docshell/test/navigation/file_beforeunload_and_bfcache.html
new file mode 100644
index 0000000000..5c5aea2918
--- /dev/null
+++ b/docshell/test/navigation/file_beforeunload_and_bfcache.html
@@ -0,0 +1,31 @@
+<html>
+ <head>
+ <script>
+ onpageshow = function(pageShowEvent) {
+ var bc = new BroadcastChannel("beforeunload_and_bfcache");
+ bc.onmessage = function(event) {
+ if (event.data == "nextpage") {
+ bc.close();
+ location.href += "?nextpage";
+ } else if (event.data == "back") {
+ bc.close();
+ history.back();
+ } else if (event.data == "forward") {
+ onbeforeunload = function() {
+ bc.postMessage("beforeunload");
+ bc.close();
+ }
+ history.forward();
+ } else if (event.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ }
+ };
+ bc.postMessage({type: pageShowEvent.type, persisted: pageShowEvent.persisted});
+ };
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_blockBFCache.html b/docshell/test/navigation/file_blockBFCache.html
new file mode 100644
index 0000000000..dc743cdc67
--- /dev/null
+++ b/docshell/test/navigation/file_blockBFCache.html
@@ -0,0 +1,33 @@
+<script>
+let keepAlive;
+window.onpageshow = (pageShow) => {
+ let bc = new BroadcastChannel("bfcache_blocking");
+
+ bc.onmessage = async function(m) {
+ switch(m.data.message) {
+ case "load":
+ bc.close();
+ location.href = m.data.url;
+ break;
+ case "runScript":
+ let test = new Function(`return ${m.data.fun};`)();
+ keepAlive = await test.call(window);
+ bc.postMessage({ type: "runScriptDone" });
+ break;
+ case "back":
+ bc.close();
+ history.back();
+ break;
+ case "forward":
+ bc.close();
+ history.forward();
+ break;
+ case "close":
+ window.close();
+ break;
+ }
+ };
+
+ bc.postMessage({ type: pageShow.type, persisted: pageShow.persisted })
+};
+</script>
diff --git a/docshell/test/navigation/file_bug1300461.html b/docshell/test/navigation/file_bug1300461.html
new file mode 100644
index 0000000000..d7abe8be90
--- /dev/null
+++ b/docshell/test/navigation/file_bug1300461.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <title>Bug 1300461</title>
+ </head>
+ <body onload="test();">
+ <script>
+ /**
+ * Bug 1300461 identifies that if a history entry was not bfcached, and
+ * a http redirection happens when navigating to that entry, the history
+ * index would mess up.
+ *
+ * The test case emulates the circumstance by the following steps
+ * 1) Navigate to file_bug1300461_back.html which is not bf-cachable.
+ * 2) In file_bug1300461_back.html, replace its own history state to
+ * file_bug1300461_redirect.html.
+ * 3) Back, and then forward. Since the document is not in bfcache, it
+ * tries to load file_bug1300461_redirect.html directly.
+ * 4) file_bug1300461_redirect.html redirects UA to
+ * file_bug1300461_back.html through HTTP 301 header.
+ *
+ * We verify the history index, canGoBack, canGoForward, etc. keep correct
+ * in this process.
+ */
+ let Ci = SpecialPowers.Ci;
+ let webNav = SpecialPowers.wrap(window)
+ .docShell
+ .QueryInterface(Ci.nsIWebNavigation);
+ let shistory = webNav.sessionHistory;
+ let testSteps = [
+ function() {
+ opener.is(shistory.count, 1, "check history length");
+ opener.is(shistory.index, 0, "check history index");
+ opener.ok(!webNav.canGoForward, "check canGoForward");
+ setTimeout(() => window.location = "file_bug1300461_back.html", 0);
+ },
+ function() {
+ opener.is(shistory.count, 2, "check history length");
+ opener.is(shistory.index, 0, "check history index");
+ opener.ok(webNav.canGoForward, "check canGoForward");
+ window.history.forward();
+ },
+ function() {
+ opener.is(shistory.count, 2, "check history length");
+ opener.is(shistory.index, 0, "check history index");
+ opener.ok(webNav.canGoForward, "check canGoForward");
+ opener.info("file_bug1300461.html tests finished");
+ opener.finishTest();
+ },
+ ];
+
+ function test() {
+ if (opener) {
+ opener.info("file_bug1300461.html test " + opener.testCount);
+ testSteps[opener.testCount++]();
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1300461_back.html b/docshell/test/navigation/file_bug1300461_back.html
new file mode 100644
index 0000000000..3ec431c7c1
--- /dev/null
+++ b/docshell/test/navigation/file_bug1300461_back.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <title>Bug 1300461</title>
+ </head>
+ <!-- The empty unload handler is to prevent bfcache. -->
+ <body onload="test();" onunload="">
+ <script>
+ let Ci = SpecialPowers.Ci;
+ let webNav = SpecialPowers.wrap(window)
+ .docShell
+ .QueryInterface(Ci.nsIWebNavigation);
+ let shistory = webNav.sessionHistory;
+ async function test() {
+ if (opener) {
+ opener.info("file_bug1300461_back.html");
+ opener.is(shistory.count, 2, "check history length");
+ opener.is(shistory.index, 1, "check history index");
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ opener.is(shistory.legacySHistory.requestedIndex, -1, "check requestedIndex");
+ } else {
+ let index = await opener.getSHRequestedIndex(SpecialPowers.wrap(window).browsingContext.id);
+ opener.is(index, -1, "check requestedIndex");
+ }
+
+ opener.ok(webNav.canGoBack, "check canGoBack");
+ if (opener.testCount == 1) {
+ opener.info("replaceState to redirect.html");
+ window.history.replaceState({}, "", "file_bug1300461_redirect.html");
+ }
+ window.history.back();
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1300461_redirect.html b/docshell/test/navigation/file_bug1300461_redirect.html
new file mode 100644
index 0000000000..979530c5cf
--- /dev/null
+++ b/docshell/test/navigation/file_bug1300461_redirect.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <title>Bug 1300461</title>
+ </head>
+ <body>
+ Redirect to file_bug1300461_back.html.
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1300461_redirect.html^headers^ b/docshell/test/navigation/file_bug1300461_redirect.html^headers^
new file mode 100644
index 0000000000..241b891826
--- /dev/null
+++ b/docshell/test/navigation/file_bug1300461_redirect.html^headers^
@@ -0,0 +1,2 @@
+HTTP 301 Moved Permanently
+Location: file_bug1300461_back.html
diff --git a/docshell/test/navigation/file_bug1326251.html b/docshell/test/navigation/file_bug1326251.html
new file mode 100644
index 0000000000..57a81f46f1
--- /dev/null
+++ b/docshell/test/navigation/file_bug1326251.html
@@ -0,0 +1,212 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Bug 1326251</title>
+ <script>
+ var bc = new BroadcastChannel("file_bug1326251");
+ bc.onmessage = function(event) {
+ if ("nextTest" in event.data) {
+ testSteps[event.data.nextTest]();
+ }
+ }
+
+ function is(val1, val2, msg) {
+ bc.postMessage({type: "is", value1: val1, value2: val2, message: msg});
+ }
+
+ function ok(val, msg) {
+ bc.postMessage({type: "ok", value: val, message: msg});
+ }
+
+ const BASE_URL = "http://mochi.test:8888/tests/docshell/test/navigation/";
+ let testSteps = [
+ async function() {
+ // Test 1: Create dynamic iframe with bfcache enabled.
+ // Navigate static / dynamic iframes, then navigate top level window
+ // and navigate back. Both iframes should still exist with history
+ // entries preserved.
+ window.onunload = null; // enable bfcache
+ await createDynamicFrame(document);
+ await loadUriInFrame(document.getElementById("staticFrame"), "frame1.html");
+ await loadUriInFrame(document.getElementById("dynamicFrame"), "frame1.html");
+ await loadUriInFrame(document.getElementById("staticFrame"), "frame2.html");
+ await loadUriInFrame(document.getElementById("dynamicFrame"), "frame2.html");
+ is(history.length, 5, "history.length");
+ window.location = "goback.html";
+ },
+ async function() {
+ let webNav = SpecialPowers.wrap(window)
+ .docShell
+ .QueryInterface(SpecialPowers.Ci.nsIWebNavigation);
+ let shistory = webNav.sessionHistory;
+ is(webNav.canGoForward, true, "canGoForward");
+ is(shistory.index, 4, "shistory.index");
+ is(history.length, 6, "history.length");
+ is(document.getElementById("staticFrame").contentWindow.location.href, BASE_URL + "frame2.html", "staticFrame location");
+ is(document.getElementById("dynamicFrame").contentWindow.location.href, BASE_URL + "frame2.html", "dynamicFrame location");
+
+ // Test 2: Load another page in dynamic iframe, canGoForward should be
+ // false.
+ await loadUriInFrame(document.getElementById("dynamicFrame"), "frame3.html");
+ is(webNav.canGoForward, false, "canGoForward");
+ is(shistory.index, 5, "shistory.index");
+ is(history.length, 6, "history.length");
+
+ // Test 3: Navigate to antoher page with bfcache disabled, all dynamic
+ // iframe entries should be removed.
+ window.onunload = function() {}; // disable bfcache
+ window.location = "goback.html";
+ },
+ async function() {
+ let windowWrap = SpecialPowers.wrap(window);
+ let docShell = windowWrap.docShell;
+ let shistory = docShell.QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
+ .sessionHistory;
+ // Now staticFrame has frame0 -> frame1 -> frame2.
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ // *EntryIndex attributes aren't meaningful when the session history
+ // lives in the parent process.
+ is(docShell.previousEntryIndex, 3, "docShell.previousEntryIndex");
+ is(docShell.loadedEntryIndex, 2, "docShell.loadedEntryIndex");
+ }
+ is(shistory.index, 2, "shistory.index");
+ is(history.length, 4, "history.length");
+ is(document.getElementById("staticFrame").contentWindow.location.href, BASE_URL + "frame2.html", "staticFrame location");
+ ok(!document.getElementById("dynamicFrame"), "dynamicFrame should not exist");
+
+ // Test 4: Load a nested frame in the static frame, navigate the inner
+ // static frame, add a inner dynamic frame and navigate the dynamic
+ // frame. Then navigate the outer static frame and go back. The inner
+ // iframe should show the last entry of inner static frame.
+ let staticFrame = document.getElementById("staticFrame");
+ staticFrame.width = "320px";
+ staticFrame.height = "360px";
+ await loadUriInFrame(staticFrame, "iframe_static.html");
+ let innerStaticFrame = staticFrame.contentDocument.getElementById("staticFrame");
+ await loadUriInFrame(innerStaticFrame, "frame1.html");
+ let innerDynamicFrame = await createDynamicFrame(staticFrame.contentDocument, "frame2.html");
+ await loadUriInFrame(innerDynamicFrame, "frame3.html");
+ // staticFrame: frame0 -> frame1 -> frame2 -> iframe_static
+ // innerStaticFrame: frame0 -> frame1
+ // innerDynamicFrame: frame2 -> frame3
+ is(shistory.index, 5, "shistory.index");
+ is(history.length, 6, "history.length");
+
+ // Wait for 2 load events - navigation and goback.
+ let onloadPromise = awaitOnload(staticFrame, 2);
+ await loadUriInFrame(staticFrame, "goback.html");
+ await onloadPromise;
+ // staticFrame: frame0 -> frame1 -> frame2 -> iframe_static -> goback
+ // innerStaticFrame: frame0 -> frame1
+ is(shistory.index, 4, "shistory.index");
+ is(history.length, 6, "history.length");
+ innerStaticFrame = staticFrame.contentDocument.getElementById("staticFrame");
+ is(innerStaticFrame.contentDocument.location.href, BASE_URL + "frame1.html", "innerStaticFrame location");
+ ok(!staticFrame.contentDocument.getElementById("dynamicFrame"), "innerDynamicFrame should not exist");
+
+ // Test 5: Insert and navigate inner dynamic frame again with bfcache
+ // enabled, and navigate top level window to a special page which will
+ // evict bfcache then goback. Verify that dynamic entries are correctly
+ // removed in this case.
+ window.onunload = null; // enable bfcache
+ staticFrame.width = "320px";
+ staticFrame.height = "360px";
+ innerDynamicFrame = await createDynamicFrame(staticFrame.contentDocument, "frame2.html");
+ await loadUriInFrame(innerDynamicFrame, "frame3.html");
+ // staticFrame: frame0 -> frame1 -> frame2 -> iframe_static
+ // innerStaticFrame: frame0 -> frame1
+ // innerDynamicFrame: frame2 -> frame3
+ is(shistory.index, 5, "shistory.index");
+ is(history.length, 6, "history.length");
+ window.location = "file_bug1326251_evict_cache.html";
+ },
+ async function() {
+ let windowWrap = SpecialPowers.wrap(window);
+ let docShell = windowWrap.docShell;
+ let shistory = docShell.QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
+ .sessionHistory;
+ // staticFrame: frame0 -> frame1 -> frame2 -> iframe_static
+ // innerStaticFrame: frame0 -> frame1
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ // *EntryIndex attributes aren't meaningful when the session history
+ // lives in the parent process.
+ is(docShell.previousEntryIndex, 5, "docShell.previousEntryIndex");
+ is(docShell.loadedEntryIndex, 4, "docShell.loadedEntryIndex");
+ }
+ is(shistory.index, 4, "shistory.index");
+ is(history.length, 6, "history.length");
+ let staticFrame = document.getElementById("staticFrame");
+ let innerStaticFrame = staticFrame.contentDocument.getElementById("staticFrame");
+ is(innerStaticFrame.contentDocument.location.href, BASE_URL + "frame1.html", "innerStaticFrame location");
+ ok(!staticFrame.contentDocument.getElementById("dynamicFrame"), "innerDynamicFrame should not exist");
+
+ // Test 6: Insert and navigate inner dynamic frame and then reload outer
+ // frame. Verify that inner dynamic frame entries are all removed.
+ staticFrame.width = "320px";
+ staticFrame.height = "360px";
+ let innerDynamicFrame = await createDynamicFrame(staticFrame.contentDocument, "frame2.html");
+ await loadUriInFrame(innerDynamicFrame, "frame3.html");
+ // staticFrame: frame0 -> frame1 -> frame2 -> iframe_static
+ // innerStaticFrame: frame0 -> frame1
+ // innerDynamicFrame: frame2 -> frame3
+ is(shistory.index, 5, "shistory.index");
+ is(history.length, 6, "history.length");
+ let staticFrameLoadPromise = new Promise(resolve => {
+ staticFrame.onload = resolve;
+ });
+ staticFrame.contentWindow.location.reload();
+ await staticFrameLoadPromise;
+ // staticFrame: frame0 -> frame1 -> frame2 -> iframe_static
+ // innerStaticFrame: frame0 -> frame1
+ is(shistory.index, 4, "shistory.index");
+ is(history.length, 5, "history.length");
+ innerStaticFrame = staticFrame.contentDocument.getElementById("staticFrame");
+ is(innerStaticFrame.contentDocument.location.href, BASE_URL + "frame1.html", "innerStaticFrame location");
+ ok(!staticFrame.contentDocument.getElementById("dynamicFrame"), "innerDynamicFrame should not exist");
+ bc.postMessage("finishTest");
+ bc.close();
+ window.close();
+ },
+ ];
+
+ function awaitOnload(frame, occurances = 1) {
+ return new Promise(function(resolve, reject) {
+ let count = 0;
+ frame.addEventListener("load", function listener(e) {
+ if (++count == occurances) {
+ frame.removeEventListener("load", listener);
+ setTimeout(resolve, 0);
+ }
+ });
+ });
+ }
+
+ async function createDynamicFrame(targetDocument, frameSrc = "frame0.html") {
+ let dynamicFrame = targetDocument.createElement("iframe");
+ let onloadPromise = awaitOnload(dynamicFrame);
+ dynamicFrame.id = "dynamicFrame";
+ dynamicFrame.src = frameSrc;
+ let container = targetDocument.getElementById("frameContainer");
+ container.appendChild(dynamicFrame);
+ await onloadPromise;
+ return dynamicFrame;
+ }
+
+ async function loadUriInFrame(frame, uri) {
+ let onloadPromise = awaitOnload(frame);
+ frame.src = uri;
+ return onloadPromise;
+ }
+
+ function test() {
+ bc.postMessage("requestNextTest");
+ }
+ </script>
+ </head>
+ <body onpageshow="test();">
+ <div id="frameContainer">
+ <iframe id="staticFrame" src="frame0.html"></iframe>
+ </div>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1326251_evict_cache.html b/docshell/test/navigation/file_bug1326251_evict_cache.html
new file mode 100644
index 0000000000..b9873947f4
--- /dev/null
+++ b/docshell/test/navigation/file_bug1326251_evict_cache.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Bug 1326251</title>
+ <script>
+ // Evict bfcache and then go back.
+ async function evictCache() {
+ await SpecialPowers.evictAllContentViewers();
+
+ history.back();
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(evictCache, 0);">
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1364364-1.html b/docshell/test/navigation/file_bug1364364-1.html
new file mode 100644
index 0000000000..d4ecc42ad4
--- /dev/null
+++ b/docshell/test/navigation/file_bug1364364-1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>title</title>
+ </head>
+ <body onload="loadFramesAndNavigate();">
+ <p id="content"></p>
+ <div id="frameContainer">
+ </div>
+ <script type="application/javascript">
+ function waitForLoad(frame) {
+ return new Promise(r => frame.onload = () => setTimeout(r, 0));
+ }
+
+ async function loadFramesAndNavigate() {
+ let dynamicFrame = document.createElement("iframe");
+ dynamicFrame.src = "data:text/html,iframe1";
+ document.querySelector("#frameContainer").appendChild(dynamicFrame);
+ await waitForLoad(dynamicFrame);
+ dynamicFrame.src = "data:text/html,iframe2";
+ await waitForLoad(dynamicFrame);
+ dynamicFrame.src = "data:text/html,iframe3";
+ await waitForLoad(dynamicFrame);
+ dynamicFrame.src = "data:text/html,iframe4";
+ await waitForLoad(dynamicFrame);
+ dynamicFrame.src = "data:text/html,iframe5";
+ await waitForLoad(dynamicFrame);
+ location.href = "file_bug1364364-2.html";
+ }
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1364364-2.html b/docshell/test/navigation/file_bug1364364-2.html
new file mode 100644
index 0000000000..6e52ecaaa9
--- /dev/null
+++ b/docshell/test/navigation/file_bug1364364-2.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>title</title>
+ </head>
+ <body onload="notifyOpener();">
+ <script type="application/javascript">
+ function notifyOpener() {
+ opener.postMessage("navigation-done", "*");
+ }
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1375833-frame1.html b/docshell/test/navigation/file_bug1375833-frame1.html
new file mode 100644
index 0000000000..ea38326479
--- /dev/null
+++ b/docshell/test/navigation/file_bug1375833-frame1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>iframe test page 1</title>
+ </head>
+ <body>iframe test page 1</body>
+</html>
diff --git a/docshell/test/navigation/file_bug1375833-frame2.html b/docshell/test/navigation/file_bug1375833-frame2.html
new file mode 100644
index 0000000000..6e76ab7e47
--- /dev/null
+++ b/docshell/test/navigation/file_bug1375833-frame2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>iframe test page 2</title>
+ </head>
+ <body>iframe test page 2</body>
+</html>
diff --git a/docshell/test/navigation/file_bug1375833.html b/docshell/test/navigation/file_bug1375833.html
new file mode 100644
index 0000000000..373a7fe08e
--- /dev/null
+++ b/docshell/test/navigation/file_bug1375833.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Test for bug 1375833</title>
+ </head>
+ <body onload="test();">
+ <iframe id="testFrame" src="file_bug1375833-frame1.html"></iframe>
+ <script type="application/javascript">
+ function test() {
+ let iframe = document.querySelector("#testFrame");
+ setTimeout(function() { iframe.src = "file_bug1375833-frame1.html"; }, 0);
+ iframe.onload = function(e) {
+ setTimeout(function() { iframe.src = "file_bug1375833-frame2.html"; }, 0);
+ iframe.onload = function() {
+ opener.postMessage(iframe.contentWindow.location.href, "*");
+ };
+ };
+ }
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1379762-1.html b/docshell/test/navigation/file_bug1379762-1.html
new file mode 100644
index 0000000000..c8cd666667
--- /dev/null
+++ b/docshell/test/navigation/file_bug1379762-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Bug 1379762</title>
+ </head>
+ <img srcset> <!-- This tries to add load blockers during bfcache restoration -->
+ <script>
+ onunload = null; // enable bfcache
+ var bc = new BroadcastChannel('bug1379762');
+ bc.postMessage("init");
+ onpageshow = function() {
+ bc.onmessage = (messageEvent) => {
+ let message = messageEvent.data;
+ if (message == "forward_back") {
+ // Navigate forward and then back.
+ // eslint-disable-next-line no-global-assign
+ setTimeout(function() { location = "goback.html"; }, 0);
+ } else if (message == "finish_test") {
+ // Do this async so our load event gets a chance to fire if it plans to
+ // do it.
+ setTimeout(function() {
+ bc.postMessage("finished");
+ bc.close();
+ window.close();
+ });
+ }
+ }
+ bc.postMessage("increment_testCount");
+ };
+ onload = function() {
+ bc.postMessage("increment_loadCount");
+ };
+ </script>
+</html>
diff --git a/docshell/test/navigation/file_bug1536471.html b/docshell/test/navigation/file_bug1536471.html
new file mode 100644
index 0000000000..53012257ee
--- /dev/null
+++ b/docshell/test/navigation/file_bug1536471.html
@@ -0,0 +1,8 @@
+<html>
+ <body onload="opener.bodyOnLoad()">
+ Nested Frame
+ <div id="frameContainer">
+ <iframe id="staticFrame" src="frame0.html"></iframe>
+ </div>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1583110.html b/docshell/test/navigation/file_bug1583110.html
new file mode 100644
index 0000000000..5b08f54d21
--- /dev/null
+++ b/docshell/test/navigation/file_bug1583110.html
@@ -0,0 +1,26 @@
+<html>
+ <head>
+ <script>
+ var bc;
+ window.onpageshow = function(pageshow) {
+ bc = new BroadcastChannel("bug1583110");
+ bc.onmessage = function(event) {
+ bc.close();
+ if (event.data == "loadnext") {
+ location.search = "?nextpage";
+ } else if (event.data == "back") {
+ history.back();
+ }
+ }
+ bc.postMessage({type: "pageshow", persisted: pageshow.persisted });
+ if (pageshow.persisted) {
+ document.body.appendChild(document.createElement("iframe"));
+ bc.close();
+ window.close();
+ }
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1609475.html b/docshell/test/navigation/file_bug1609475.html
new file mode 100644
index 0000000000..7699d46b08
--- /dev/null
+++ b/docshell/test/navigation/file_bug1609475.html
@@ -0,0 +1,51 @@
+<html>
+ <head>
+ <script>
+
+ var loadCount = 0;
+ function loadListener(event) {
+ ++loadCount;
+ if (loadCount == 2) {
+ // Use a timer to ensure we don't get extra load events.
+ setTimeout(function() {
+ var doc1URI = document.getElementById("i1").contentDocument.documentURI;
+ opener.ok(doc1URI.includes("frame1.html"),
+ "Should have loaded the initial page to the first iframe. Got " + doc1URI);
+ var doc2URI = document.getElementById("i2").contentDocument.documentURI;
+ opener.ok(doc2URI.includes("frame1.html"),
+ "Should have loaded the initial page to the second iframe. Got " + doc2URI);
+ opener.finishTest();
+ }, 1000);
+ } else if (loadCount > 2) {
+ opener.ok(false, "Too many load events");
+ }
+ // if we don't get enough load events, the test will time out.
+ }
+
+ function setupIframe(id) {
+ var ifr = document.getElementById(id);
+ return new Promise(function(resolve) {
+ ifr.onload = function() {
+ // Replace load listener to catch page loads from the session history.
+ ifr.onload = loadListener;
+ // Need to use setTimeout, because triggering loads inside
+ // load event listener has special behavior since at the moment
+ // the docshell keeps track of whether it is executing a load handler or not.
+ setTimeout(resolve);
+ }
+ ifr.contentWindow.location.href = "frame2.html";
+ });
+ }
+
+ async function test() {
+ await setupIframe("i1");
+ await setupIframe("i2");
+ history.go(-2);
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(test)">
+ <iframe id="i1" src="frame1.html"></iframe>
+ <iframe id="i2" src="frame1.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/file_bug1706090.html b/docshell/test/navigation/file_bug1706090.html
new file mode 100644
index 0000000000..9c5bc025d3
--- /dev/null
+++ b/docshell/test/navigation/file_bug1706090.html
@@ -0,0 +1,40 @@
+<html>
+ <head>
+ <script>
+ onpageshow = function(pageShowEvent) {
+ if (location.hostname == "example.com" ||
+ location.hostname == "test1.mochi.test") {
+ // BroadcastChannel doesn't work across domains, so just go to the
+ // previous page explicitly.
+ history.back();
+ return;
+ }
+
+ var bc = new BroadcastChannel("bug1706090");
+ bc.onmessage = function(event) {
+ if (event.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ } else if (event.data == "sameOrigin") {
+ bc.close();
+ location.href = location.href + "?foo"
+ } else if (event.data == "back") {
+ history.back();
+ } else if (event.data == "crossOrigin") {
+ bc.close();
+ location.href = "https://example.com:443" + location.pathname;
+ } else if (event.data == "sameSite") {
+ bc.close();
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ location.href = "http://test1.mochi.test:8888" + location.pathname;
+ }
+ }
+
+ bc.postMessage({type: pageShowEvent.type, persisted: pageShowEvent.persisted});
+ }
+ </script>
+ </head>
+ <body onunload="/* dummy listener*/">
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1745638.html b/docshell/test/navigation/file_bug1745638.html
new file mode 100644
index 0000000000..b98c8de91f
--- /dev/null
+++ b/docshell/test/navigation/file_bug1745638.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<script>
+ function go() {
+ var doc = document.getElementById("testFrame").contentWindow.document;
+ doc.open();
+ doc.close();
+ doc.body.innerText = "passed";
+ }
+</script>
+<body onload="setTimeout(opener.pageLoaded);">
+The iframe below should not be blank after a reload.<br>
+<iframe src="about:blank" id="testFrame"></iframe>
+<script>
+ go();
+</script>
diff --git a/docshell/test/navigation/file_bug1750973.html b/docshell/test/navigation/file_bug1750973.html
new file mode 100644
index 0000000000..28b2f995ae
--- /dev/null
+++ b/docshell/test/navigation/file_bug1750973.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script>
+ function test() {
+ history.scrollRestoration = "manual";
+ document.getElementById("initialfocus").focus();
+ history.pushState('data', '', '');
+ history.back();
+ }
+
+ window.onpopstate = function() {
+ window.onscroll = function() {
+ window.onscroll = null;
+ document.documentElement.style.display = "none";
+ // focus() triggers recreation of the nsIFrames without a reflow.
+ document.getElementById("focustarget").focus();
+ document.documentElement.style.display = "";
+ // Flush the layout.
+ document.documentElement.getBoundingClientRect();
+ opener.isnot(window.scrollY, 0, "The page should have been scrolled down(1)");
+ requestAnimationFrame(
+ function() {
+ // Extra timeout to ensure we're called after rAF has triggered a
+ // reflow.
+ setTimeout(function() {
+ opener.isnot(window.scrollY, 0, "The page should have been scrolled down(2)");
+ window.close();
+ opener.SimpleTest.finish();
+ });
+ });
+ }
+ window.scrollTo(0, 1000);
+ }
+ </script>
+</head>
+<body onload="setTimeout(test)">
+<div style="position: fixed;">
+ <input type="button" value="" id="initialfocus">
+ <input type="button" value="" id="focustarget">
+</div>
+<div style="border: 1px solid black; width: 100px; height: 9000px;"></div>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_bug1758664.html b/docshell/test/navigation/file_bug1758664.html
new file mode 100644
index 0000000000..07798dfddd
--- /dev/null
+++ b/docshell/test/navigation/file_bug1758664.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+var onIframeOnload = function() {
+ var iframe = window.document.getElementById('applicationIframe');
+ opener.is(iframe.contentWindow.location.search, "?iframe", "Should have loaded the iframe");
+ window.close();
+ opener.SimpleTest.finish();
+}
+
+var onPageOnload = function() {
+ if (location.search == "?iframe") {
+ return;
+ }
+ if(!window.name) {
+ window.name = 'file_bug1758664.html';
+ window.location.reload();
+ return;
+ }
+ var iframe = window.document.getElementById('applicationIframe');
+ iframe.addEventListener('load', onIframeOnload);
+ iframe.src = "file_bug1758664.html?iframe";
+}
+window.document.addEventListener("DOMContentLoaded", onPageOnload);
+
+</script>
+</head>
+<body>
+ <iframe id="applicationIframe"></iframe>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_bug386782_contenteditable.html b/docshell/test/navigation/file_bug386782_contenteditable.html
new file mode 100644
index 0000000000..4515d015d9
--- /dev/null
+++ b/docshell/test/navigation/file_bug386782_contenteditable.html
@@ -0,0 +1 @@
+<html><head><meta charset="utf-8"><script>window.addEventListener("pageshow", function(event) { window.opener.postMessage({persisted: event.persisted}, "*"); });</script></head><body contentEditable="true"><p>contentEditable</p></body></html> \ No newline at end of file
diff --git a/docshell/test/navigation/file_bug386782_designmode.html b/docshell/test/navigation/file_bug386782_designmode.html
new file mode 100644
index 0000000000..faa063cbae
--- /dev/null
+++ b/docshell/test/navigation/file_bug386782_designmode.html
@@ -0,0 +1 @@
+<html><head><meta charset="utf-8"><script>window.addEventListener("pageshow", function(event) { window.opener.postMessage({persisted: event.persisted}, "*"); });</script></head><body><p>designModeDocument</p></body></html> \ No newline at end of file
diff --git a/docshell/test/navigation/file_bug462076_1.html b/docshell/test/navigation/file_bug462076_1.html
new file mode 100644
index 0000000000..5050e79fdc
--- /dev/null
+++ b/docshell/test/navigation/file_bug462076_1.html
@@ -0,0 +1,55 @@
+<html>
+ <head>
+ <title>Bug 462076</title>
+ <script>
+ var srcs = [ "frame0.html",
+ "frame1.html",
+ "frame2.html",
+ "frame3.html" ];
+
+ var checkCount = 0;
+
+ function makeFrame(index) {
+ var ifr = document.createElement("iframe");
+ ifr.src = srcs[index];
+ ifr.onload = checkFrame;
+ document.getElementById("container" + index).appendChild(ifr);
+ }
+
+ function runTest() {
+ var randomNumber = Math.floor(Math.random() * 4);
+ for (var i = randomNumber; i < 4; ++i) {
+ makeFrame(i);
+ }
+ for (var k = 0; k < randomNumber; ++k) {
+ makeFrame(k);
+ }
+ }
+
+ function checkFrame(evt) {
+ var ifr = evt.target;
+ opener.ok(String(ifr.contentWindow.location).includes(ifr.src),
+ "Wrong document loaded (" + ifr.src + ", " +
+ ifr.contentWindow.location + ")!");
+
+ if (++checkCount == 4) {
+ if (++opener.testCount == 10) {
+ opener.nextTest();
+ window.close();
+ } else {
+ window.location.reload();
+ }
+ }
+ }
+ </script>
+ </head>
+ <body>
+ <div id="container0"></div>
+ <div id="container1"></div>
+ <div id="container2"></div>
+ <div id="container3"></div>
+ <script>
+ runTest();
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug462076_2.html b/docshell/test/navigation/file_bug462076_2.html
new file mode 100644
index 0000000000..63cf3de3f9
--- /dev/null
+++ b/docshell/test/navigation/file_bug462076_2.html
@@ -0,0 +1,52 @@
+<html>
+ <head>
+ <title>Bug 462076</title>
+ <script>
+ var srcs = [ "frame0.html",
+ "frame1.html",
+ "frame2.html",
+ "frame3.html" ];
+
+ var checkCount = 0;
+
+ function makeFrame(index) {
+ var ifr = document.createElement("iframe");
+ ifr.src = srcs[index];
+ ifr.onload = checkFrame;
+ document.getElementById("container" + index).appendChild(ifr);
+ }
+
+ function runTest() {
+ var randomNumber = Math.floor(Math.random() * 4);
+ for (var i = randomNumber; i < 4; ++i) {
+ makeFrame(i);
+ }
+ for (var k = 0; k < randomNumber; ++k) {
+ makeFrame(k);
+ }
+ }
+
+ function checkFrame(evt) {
+ var ifr = evt.target;
+ opener.ok(String(ifr.contentWindow.location).includes(ifr.src),
+ "Wrong document loaded (" + ifr.src + ", " +
+ ifr.contentWindow.location + ")!");
+
+ if (++checkCount == 4) {
+ if (++opener.testCount == 10) {
+ opener.nextTest();
+ window.close();
+ } else {
+ window.location.reload();
+ }
+ }
+ }
+ </script>
+ </head>
+ <body onload="runTest();">
+ <div id="container0"></div>
+ <div id="container1"></div>
+ <div id="container2"></div>
+ <div id="container3"></div>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug462076_3.html b/docshell/test/navigation/file_bug462076_3.html
new file mode 100644
index 0000000000..5c779d2f49
--- /dev/null
+++ b/docshell/test/navigation/file_bug462076_3.html
@@ -0,0 +1,52 @@
+<html>
+ <head>
+ <title>Bug 462076</title>
+ <script>
+ var srcs = [ "frame0.html",
+ "frame1.html",
+ "frame2.html",
+ "frame3.html" ];
+
+ var checkCount = 0;
+
+ function makeFrame(index) {
+ var ifr = document.createElement("iframe");
+ ifr.src = srcs[index];
+ ifr.onload = checkFrame;
+ document.getElementById("container" + index).appendChild(ifr);
+ }
+
+ function runTest() {
+ var randomNumber = Math.floor(Math.random() * 4);
+ for (var i = randomNumber; i < 4; ++i) {
+ makeFrame(i);
+ }
+ for (var k = 0; k < randomNumber; ++k) {
+ makeFrame(k);
+ }
+ }
+
+ function checkFrame(evt) {
+ var ifr = evt.target;
+ opener.ok(String(ifr.contentWindow.location).includes(ifr.src),
+ "Wrong document loaded (" + ifr.src + ", " +
+ ifr.contentWindow.location + ")!");
+
+ if (++checkCount == 4) {
+ if (++opener.testCount == 10) {
+ opener.nextTest();
+ window.close();
+ } else {
+ window.location.reload();
+ }
+ }
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(runTest, 0);">
+ <div id="container0"></div>
+ <div id="container1"></div>
+ <div id="container2"></div>
+ <div id="container3"></div>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug508537_1.html b/docshell/test/navigation/file_bug508537_1.html
new file mode 100644
index 0000000000..182c085670
--- /dev/null
+++ b/docshell/test/navigation/file_bug508537_1.html
@@ -0,0 +1,33 @@
+<html>
+ <head>
+ <script>
+ function dynFrameLoad() {
+ var ifrs = document.getElementsByTagName("iframe");
+ opener.ok(String(ifrs[0].contentWindow.location).includes(ifrs[0].src),
+ "Wrong document loaded (1)\n");
+ opener.ok(String(ifrs[1].contentWindow.location).includes(ifrs[1].src),
+ "Wrong document loaded (2)\n");
+ if (opener && ++opener.testCount == 1) {
+ window.location = "goback.html";
+ } else {
+ opener.finishTest();
+ }
+ }
+
+ window.addEventListener("load",
+ function() {
+ var container = document.getElementById("t1");
+ container.addEventListener("load", dynFrameLoad, true);
+ container.appendChild(container.appendChild(document.getElementById("i1")));
+ });
+ </script>
+ </head>
+ <body>
+ <h5>Container:</h5>
+ <div id="t1"></div>
+ <h5>Original frames:</h5>
+ <iframe id="i1" src="frame0.html"></iframe>
+ <iframe src="frame1.html"></iframe>
+ </body>
+</html>
+
diff --git a/docshell/test/navigation/file_bug534178.html b/docshell/test/navigation/file_bug534178.html
new file mode 100644
index 0000000000..4d77dd824b
--- /dev/null
+++ b/docshell/test/navigation/file_bug534178.html
@@ -0,0 +1,30 @@
+<html>
+ <head>
+ <script>
+
+ function testDone() {
+ document.body.firstChild.remove();
+ var isOK = false;
+ try {
+ isOK = history.previous != location;
+ } catch (ex) {
+ // history.previous should throw if this is the first page in shistory.
+ isOK = true;
+ }
+ document.body.textContent = isOK ? "PASSED" : "FAILED";
+ opener.ok(isOK, "Duplicate session history entries should have been removed!");
+ opener.finishTest();
+ }
+ function ifrload() {
+ setTimeout(testDone, 0);
+ }
+ function test() {
+ var ifr = document.getElementsByTagName("iframe")[0];
+ ifr.onload = ifrload;
+ ifr.src = "data:text/html,doc2";
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(test, 0)"><iframe src="data:text/html,doc1"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_contentpolicy_block_window.html b/docshell/test/navigation/file_contentpolicy_block_window.html
new file mode 100644
index 0000000000..c51e574e5e
--- /dev/null
+++ b/docshell/test/navigation/file_contentpolicy_block_window.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+This window should never be openend!
+</body>
+</html>
diff --git a/docshell/test/navigation/file_docshell_gotoindex.html b/docshell/test/navigation/file_docshell_gotoindex.html
new file mode 100644
index 0000000000..f3e8919822
--- /dev/null
+++ b/docshell/test/navigation/file_docshell_gotoindex.html
@@ -0,0 +1,42 @@
+<html>
+ <head>
+ <script>
+ function loaded() {
+ if (location.search == "") {
+ if (opener.loadedInitialPage) {
+ opener.ok(true, "got back to the initial page.");
+ opener.setTimeout("SimpleTest.finish();");
+ window.close();
+ return;
+ }
+ opener.loadedInitialPage = true;
+ opener.info("Loaded initial page.");
+ // Load another page (which is this same document, but different URL.)
+ location.href = location.href + "?anotherPage";
+ } else {
+ opener.info("Loaded the second page.");
+ location.hash = "1";
+ window.onhashchange = function() {
+ opener.info("hash: " + location.hash);
+ location.hash = "2";
+ window.onhashchange = function() {
+ opener.info("hash: " + location.hash);
+ var docShell = SpecialPowers.wrap(window).docShell;
+ var webNavigation =
+ SpecialPowers.do_QueryInterface(docShell, "nsIWebNavigation");
+ webNavigation.gotoIndex(history.length - 2);
+ window.onhashchange = function() {
+ opener.info("hash: " + location.hash);
+ webNavigation.gotoIndex(history.length - 4);
+ }
+ }
+ }
+ }
+ }
+ </script>
+ </head>
+ <body onpageshow="setTimeout(loaded)">
+ <a href="#1" name="1">1</a>
+ <a href="#2" name="2">2</a>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/file_document_write_1.html b/docshell/test/navigation/file_document_write_1.html
new file mode 100644
index 0000000000..be52b60231
--- /dev/null
+++ b/docshell/test/navigation/file_document_write_1.html
@@ -0,0 +1,18 @@
+<html>
+ <head>
+ <script>
+ function start() {
+ var length = history.length;
+ document.open();
+ document.write("<h5 id='dynamic'>document.written content</h5>");
+ document.close();
+ opener.is(history.length, length,
+ "document.open/close should not change history");
+ opener.finishTest();
+ }
+ </script>
+ </head>
+ <body onload="start();">
+ <h5>static content</h5>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_evict_from_bfcache.html b/docshell/test/navigation/file_evict_from_bfcache.html
new file mode 100644
index 0000000000..9f50533543
--- /dev/null
+++ b/docshell/test/navigation/file_evict_from_bfcache.html
@@ -0,0 +1,29 @@
+<html>
+ <head>
+ <script>
+ onpageshow = function(pageShowEvent) {
+ var bc = new BroadcastChannel("evict_from_bfcache");
+ bc.onmessage = function(event) {
+ if (event.data == "nextpage") {
+ bc.close();
+ location.href += "?nextpage";
+ } else if (event.data == "back") {
+ bc.close();
+ history.back();
+ } else if (event.data == "forward") {
+ // Note, we don't close BroadcastChannel
+ history.forward();
+ } else if (event.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ }
+ }
+
+ bc.postMessage({ type: "pageshow", persisted: pageShowEvent.persisted});
+ };
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_fragment_handling_during_load.html b/docshell/test/navigation/file_fragment_handling_during_load.html
new file mode 100644
index 0000000000..a7f468c32d
--- /dev/null
+++ b/docshell/test/navigation/file_fragment_handling_during_load.html
@@ -0,0 +1,27 @@
+<html>
+ <head>
+ <script>
+ function checkHaveLoadedNewDoc() {
+ let l = document.body.firstChild.contentWindow.location.href;
+ if (!l.endsWith("file_fragment_handling_during_load_frame2.sjs")) {
+ opener.ok(true, "Fine. We will check later.");
+ setTimeout(checkHaveLoadedNewDoc, 500);
+ return;
+ }
+ opener.ok(true, "Have loaded a new document.");
+ opener.finishTest();
+ }
+ function test() {
+ // Test that executing back() before load has started doesn't interrupt
+ // the load.
+ var ifr = document.getElementsByTagName("iframe")[0];
+ ifr.onload = checkHaveLoadedNewDoc;
+ ifr.contentWindow.location.hash = "b";
+ ifr.contentWindow.location.href = "file_fragment_handling_during_load_frame2.sjs";
+ history.back();
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(test, 0)"><iframe src="file_fragment_handling_during_load_frame1.html#a"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_fragment_handling_during_load_frame1.html b/docshell/test/navigation/file_fragment_handling_during_load_frame1.html
new file mode 100644
index 0000000000..c03ba2bda6
--- /dev/null
+++ b/docshell/test/navigation/file_fragment_handling_during_load_frame1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+foo
+</body>
+</html>
diff --git a/docshell/test/navigation/file_fragment_handling_during_load_frame2.sjs b/docshell/test/navigation/file_fragment_handling_during_load_frame2.sjs
new file mode 100644
index 0000000000..77abe5949e
--- /dev/null
+++ b/docshell/test/navigation/file_fragment_handling_during_load_frame2.sjs
@@ -0,0 +1,20 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80 ft=javascript: */
+"use strict";
+
+function handleRequest(request, response) {
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ // Wait a bit.
+ var s = Date.now();
+ // eslint-disable-next-line no-empty
+ while (Date.now() - s < 1000) {}
+
+ response.write(`<!DOCTYPE HTML>
+ <html>
+ <body>
+ bar
+ </body>
+ </html>
+ `);
+}
diff --git a/docshell/test/navigation/file_load_history_entry_page_with_one_link.html b/docshell/test/navigation/file_load_history_entry_page_with_one_link.html
new file mode 100644
index 0000000000..a4d1b62176
--- /dev/null
+++ b/docshell/test/navigation/file_load_history_entry_page_with_one_link.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body onpageshow="opener.bodyOnLoad()">
+<a id="link1" href="#1">Link 1</a>
+<a name="1">link 1</a>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_load_history_entry_page_with_two_links.html b/docshell/test/navigation/file_load_history_entry_page_with_two_links.html
new file mode 100644
index 0000000000..4be2ea6f4e
--- /dev/null
+++ b/docshell/test/navigation/file_load_history_entry_page_with_two_links.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body onpageshow="opener.bodyOnLoad()">
+<a id="link1" href="#1">Link 1</a>
+<a id="link2" href="#2">Link 2</a>
+<a name="1">link 1</a>
+<a name="2">link 2</a>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_meta_refresh.html b/docshell/test/navigation/file_meta_refresh.html
new file mode 100644
index 0000000000..2a06cc5acf
--- /dev/null
+++ b/docshell/test/navigation/file_meta_refresh.html
@@ -0,0 +1,40 @@
+<html>
+ <head>
+ </head>
+ <body>
+ <script>
+ function addMetaRefresh() {
+ // eslint-disable-next-line no-unsanitized/property
+ document.head.innerHTML = "<meta http-equiv='refresh' content='5; url=" +
+ location.href.replace("?initial", "?refresh") + "'>";
+ }
+
+ onpageshow = function() {
+ let bc = new BroadcastChannel("test_meta_refresh");
+ bc.onmessage = function(event) {
+ if (event.data == "loadnext") {
+ bc.close();
+ addMetaRefresh();
+ location.href = location.href.replace("?initial", "?nextpage");
+ } else if (event.data == "back") {
+ bc.close();
+ history.back();
+ } else if (event.data == "ensuremetarefresh") {
+ bc.close();
+ // This test is designed to work with and without bfcache, but
+ // if bfcache is enabled, meta refresh should be suspended/resumed.
+ if (document.head.firstChild.localName != "meta") {
+ addMetaRefresh();
+ }
+ } else if (event.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ }
+ };
+
+ bc.postMessage({ load: location.search.substr(1) });
+ }
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_navigation_type.html b/docshell/test/navigation/file_navigation_type.html
new file mode 100644
index 0000000000..bb538eefec
--- /dev/null
+++ b/docshell/test/navigation/file_navigation_type.html
@@ -0,0 +1,25 @@
+<html>
+ <head>
+ <script>
+ onpageshow = function() {
+ let bc = new BroadcastChannel("navigation_type");
+ bc.onmessage = function(event) {
+ if (event.data == "loadNewPage") {
+ bc.close();
+ location.href = location.href + "?next";
+ } else if (event.data == "back") {
+ bc.close();
+ history.back();
+ } else if (event.data == "close") {
+ window.close();
+ bc.postMessage("closed");
+ bc.close();
+ }
+ }
+ bc.postMessage({ navigationType: performance.navigation.type });
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/file_nested_frames.html b/docshell/test/navigation/file_nested_frames.html
new file mode 100644
index 0000000000..6ec286aa3e
--- /dev/null
+++ b/docshell/test/navigation/file_nested_frames.html
@@ -0,0 +1,27 @@
+<html>
+ <head>
+ <script>
+ function nestedIframeLoaded() {
+ var tf = document.getElementById("testframe");
+ var innerf = tf.contentDocument.getElementsByTagName("iframe")[0];
+ if (!innerf.contentDocument.documentURI.includes("frame0")) {
+ innerf.contentWindow.location.href = "http://mochi.test:8888/tests/docshell/test/navigation/frame0.html";
+ return;
+ }
+ innerf.onload = null;
+ innerf.src = "about:blank";
+ var d = innerf.contentDocument;
+ d.open();
+ d.write("test");
+ d.close();
+ opener.is(window.history.length, 1, "Unexpected history length");
+ opener.finishTest();
+ }
+ </script>
+ </head>
+ <body>
+ <iframe id="testframe" src="file_nested_frames_innerframe.html" onload="frameLoaded()"></iframe>
+ <script>
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_nested_frames_innerframe.html b/docshell/test/navigation/file_nested_frames_innerframe.html
new file mode 100644
index 0000000000..e25b6a4f6a
--- /dev/null
+++ b/docshell/test/navigation/file_nested_frames_innerframe.html
@@ -0,0 +1 @@
+<iframe onload='parent.nestedIframeLoaded();'></iframe>
diff --git a/docshell/test/navigation/file_nested_srcdoc.html b/docshell/test/navigation/file_nested_srcdoc.html
new file mode 100644
index 0000000000..ae6d656f27
--- /dev/null
+++ b/docshell/test/navigation/file_nested_srcdoc.html
@@ -0,0 +1,3 @@
+
+iframe loaded inside of a srcdoc
+<iframe id="static" srcdoc="Second nested srcdoc<iframe id='static' srcdoc='Third nested srcdoc'&gt;</iframe&gt;"></iframe>
diff --git a/docshell/test/navigation/file_new_shentry_during_history_navigation_1.html b/docshell/test/navigation/file_new_shentry_during_history_navigation_1.html
new file mode 100644
index 0000000000..2f9a41e1d1
--- /dev/null
+++ b/docshell/test/navigation/file_new_shentry_during_history_navigation_1.html
@@ -0,0 +1,5 @@
+<script>
+ onload = function() {
+ opener.postMessage("load");
+ }
+</script>
diff --git a/docshell/test/navigation/file_new_shentry_during_history_navigation_1.html^headers^ b/docshell/test/navigation/file_new_shentry_during_history_navigation_1.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/navigation/file_new_shentry_during_history_navigation_1.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/navigation/file_new_shentry_during_history_navigation_2.html b/docshell/test/navigation/file_new_shentry_during_history_navigation_2.html
new file mode 100644
index 0000000000..de456f8f1c
--- /dev/null
+++ b/docshell/test/navigation/file_new_shentry_during_history_navigation_2.html
@@ -0,0 +1,10 @@
+<script>
+ onload = function() {
+ opener.postMessage("load");
+ }
+
+ onbeforeunload = function() {
+ opener.postMessage("beforeunload");
+ history.pushState("data1", "", "?push1");
+ }
+</script>
diff --git a/docshell/test/navigation/file_new_shentry_during_history_navigation_2.html^headers^ b/docshell/test/navigation/file_new_shentry_during_history_navigation_2.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/navigation/file_new_shentry_during_history_navigation_2.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/navigation/file_new_shentry_during_history_navigation_3.html b/docshell/test/navigation/file_new_shentry_during_history_navigation_3.html
new file mode 100644
index 0000000000..2237e3e367
--- /dev/null
+++ b/docshell/test/navigation/file_new_shentry_during_history_navigation_3.html
@@ -0,0 +1,22 @@
+<script>
+ window.onpageshow = function(e) {
+ if (location.search == "?v2") {
+ onbeforeunload = function() {
+ history.pushState("data1", "", "?push1");
+ }
+ }
+
+ let bc = new BroadcastChannel("new_shentry_during_history_navigation");
+ bc.onmessage = function(event) {
+ bc.close();
+ if (event.data == "loadnext") {
+ location.href = "file_new_shentry_during_history_navigation_4.html";
+ } else if (event.data == "back") {
+ history.back();
+ } else if (event.data == "close") {
+ window.close();
+ }
+ }
+ bc.postMessage({page: location.href, type: e.type, persisted: e.persisted});
+ }
+</script>
diff --git a/docshell/test/navigation/file_new_shentry_during_history_navigation_3.html^headers^ b/docshell/test/navigation/file_new_shentry_during_history_navigation_3.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/navigation/file_new_shentry_during_history_navigation_3.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/navigation/file_new_shentry_during_history_navigation_4.html b/docshell/test/navigation/file_new_shentry_during_history_navigation_4.html
new file mode 100644
index 0000000000..d5c3f61794
--- /dev/null
+++ b/docshell/test/navigation/file_new_shentry_during_history_navigation_4.html
@@ -0,0 +1,16 @@
+<script>
+ window.onpageshow = function(e) {
+ let bc = new BroadcastChannel("new_shentry_during_history_navigation");
+ bc.onmessage = function(event) {
+ bc.close();
+ if (event.data == "loadnext") {
+ location.href = "file_new_shentry_during_history_navigation_3.html?v2";
+ } else if (event.data == "forward") {
+ history.forward();
+ } else if (event.data == "close") {
+ window.close();
+ }
+ }
+ bc.postMessage({page: location.href, type: e.type, persisted: e.persisted});
+ }
+</script>
diff --git a/docshell/test/navigation/file_online_offline_bfcache.html b/docshell/test/navigation/file_online_offline_bfcache.html
new file mode 100644
index 0000000000..7f8138e758
--- /dev/null
+++ b/docshell/test/navigation/file_online_offline_bfcache.html
@@ -0,0 +1,41 @@
+<html>
+ <head>
+ <script>
+ onpageshow = function(pageshowEvent) {
+ let bc = new BroadcastChannel("online_offline_bfcache");
+ bc.onmessage = function(event) {
+ if (event.data == "nextpage") {
+ bc.close();
+ location.href = location.href + "?nextpage";
+ } else if (event.data == "back") {
+ bc.close();
+ history.back();
+ } else if (event.data == "forward") {
+ bc.close();
+ history.forward();
+ } else if (event.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ }
+ };
+ bc.postMessage({ event: pageshowEvent.type, persisted: pageshowEvent.persisted });
+ }
+
+ onoffline = function(event) {
+ let bc = new BroadcastChannel("online_offline_bfcache");
+ bc.postMessage(event.type);
+ bc.close();
+ }
+
+ ononline = function(event) {
+ let bc = new BroadcastChannel("online_offline_bfcache");
+ bc.postMessage(event.type);
+ bc.close();
+ }
+
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_reload.html b/docshell/test/navigation/file_reload.html
new file mode 100644
index 0000000000..f0cb1c2d52
--- /dev/null
+++ b/docshell/test/navigation/file_reload.html
@@ -0,0 +1,23 @@
+<html>
+ <head>
+ <script>
+ window.onpageshow = function() {
+ let bc = new BroadcastChannel("test_reload");
+ bc.onmessage = function(event) {
+ if (event.data == "reload") {
+ bc.close();
+ location.reload(true);
+ } else if (event.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ }
+ }
+
+ bc.postMessage("pageshow");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_reload_large_postdata.sjs b/docshell/test/navigation/file_reload_large_postdata.sjs
new file mode 100644
index 0000000000..385d43d99f
--- /dev/null
+++ b/docshell/test/navigation/file_reload_large_postdata.sjs
@@ -0,0 +1,46 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80 ft=javascript: */
+"use strict";
+
+const BinaryInputStream = Components.Constructor(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+Cu.importGlobalProperties(["URLSearchParams"]);
+
+function readStream(inputStream) {
+ let available = 0;
+ let result = [];
+ while ((available = inputStream.available()) > 0) {
+ result.push(inputStream.readBytes(available));
+ }
+ return result.join("");
+}
+
+function handleRequest(request, response) {
+ let rv = (() => {
+ try {
+ if (request.method != "POST") {
+ return "ERROR: not a POST request";
+ }
+
+ let body = new URLSearchParams(
+ readStream(new BinaryInputStream(request.bodyInputStream))
+ );
+ return body.get("payload").length;
+ } catch (e) {
+ return "ERROR: Exception: " + e;
+ }
+ })();
+
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.write(`<!DOCTYPE HTML>
+<script>
+let rv = (${JSON.stringify(rv)});
+opener.postMessage(rv, "*");
+</script>
+ `);
+}
diff --git a/docshell/test/navigation/file_reload_nonbfcached_srcdoc.sjs b/docshell/test/navigation/file_reload_nonbfcached_srcdoc.sjs
new file mode 100644
index 0000000000..e1ef75efa0
--- /dev/null
+++ b/docshell/test/navigation/file_reload_nonbfcached_srcdoc.sjs
@@ -0,0 +1,27 @@
+const createPage = function (msg) {
+ return `
+<html>
+<script>
+ onpageshow = function() {
+ opener.postMessage(document.body.firstChild.contentDocument.body.textContent);
+ }
+</script>
+<body><iframe srcdoc="${msg}"></iframe><body>
+</html>
+`;
+};
+
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-store");
+ response.setHeader("Content-Type", "text/html");
+
+ let currentState = getState("reload_nonbfcached_srcdoc");
+ let srcdoc = "pageload:" + currentState;
+ if (currentState != "second") {
+ setState("reload_nonbfcached_srcdoc", "second");
+ } else {
+ setState("reload_nonbfcached_srcdoc", "");
+ }
+
+ response.write(createPage(srcdoc));
+}
diff --git a/docshell/test/navigation/file_same_url.html b/docshell/test/navigation/file_same_url.html
new file mode 100644
index 0000000000..72a1dd2564
--- /dev/null
+++ b/docshell/test/navigation/file_same_url.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+let bc = new BroadcastChannel("test_same_url");
+let listener = e => {
+ switch (e.data) {
+ case "linkClick":
+ var link = document.getElementById("link1");
+ link.click();
+ break;
+ case "closeWin":
+ self.close();
+ break;
+ }
+};
+bc.addEventListener("message", listener);
+</script>
+</head>
+<body onpageshow="bc.postMessage({bodyOnLoad: history.length})">
+<a id="link1" href="file_same_url.html">Link 1</a>
+<a name="1">link 1</a>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache.html b/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache.html
new file mode 100644
index 0000000000..fec51f821d
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache.html
@@ -0,0 +1,30 @@
+<html>
+ <head>
+ <script>
+ // Ensure layout is flushed before doing anything with scrolling.
+ function flushAndExecute(callback) {
+ window.requestAnimationFrame(function() {
+ setTimeout(callback);
+ });
+ }
+
+ var bc = new BroadcastChannel("bfcached");
+ bc.onmessage = (msgEvent) => {
+ if (msgEvent.data == "loadNext") {
+ flushAndExecute(() => {
+ location.href = "file_scrollRestoration_bfcache_and_nobfcache_part2.html";
+ });
+ } else if (msgEvent.data == "forward") {
+ flushAndExecute(() => {
+ history.forward();
+ });
+ }
+ };
+ window.onpageshow = (event) => {
+ bc.postMessage({command: "pageshow", persisted: event.persisted});
+ };
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html b/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html
new file mode 100644
index 0000000000..40e0578515
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html
@@ -0,0 +1,35 @@
+<html>
+ <head>
+ <script>
+ // Note, this page does not enter bfcache because of an HTTP header.
+
+ // Ensure layout is flushed before doing anything with scrolling.
+ function flushAndExecute(callback) {
+ window.requestAnimationFrame(function() {
+ setTimeout(callback);
+ });
+ }
+
+ var bc = new BroadcastChannel("notbfcached");
+ bc.onmessage = (msgEvent) => {
+ if (msgEvent.data == "scroll") {
+ flushAndExecute(() => { window.scrollTo(0, 4000); });
+ } else if (msgEvent.data == "getScrollY") {
+ flushAndExecute(() => { bc.postMessage({ scrollY: window.scrollY}); });
+ } else if (msgEvent.data == "back") {
+ flushAndExecute(() => { bc.close(); history.back(); });
+ } else if (msgEvent.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ }
+ };
+ window.onpageshow = (event) => {
+ bc.postMessage({command: "pageshow", persisted: event.persisted});
+ };
+ </script>
+ </head>
+ <body>
+ <div style="height: 5000px; border: 1px solid black;">content</div>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html^headers^ b/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/navigation/file_scrollRestoration_navigate.html b/docshell/test/navigation/file_scrollRestoration_navigate.html
new file mode 100644
index 0000000000..ac78f0abbe
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_navigate.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<script>
+ var bc = new BroadcastChannel("navigate");
+ window.onload = () => {
+ bc.onmessage = (event) => {
+ if (event.data.command == "navigate") {
+ window.location = event.data.location;
+ bc.close();
+ }
+ if (event.data.command == "back") {
+ history.back();
+ bc.close();
+ }
+ }
+ bc.postMessage({command: "loaded", scrollRestoration: history.scrollRestoration});
+ }
+</script> \ No newline at end of file
diff --git a/docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html b/docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html
new file mode 100644
index 0000000000..1c94899ac2
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html
@@ -0,0 +1,63 @@
+<html>
+ <head>
+ <script>
+ var oldHistoryObject = null;
+ var bc = new BroadcastChannel("bug1155730_part1");
+ bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "test") {
+ var currentCase = msg.currentCase;
+ test(currentCase);
+ }
+ }
+
+ function test(currentCase) {
+ var assertIs = [];
+ var assertOk = [];
+ var assertIsNot = [];
+ switch (currentCase) {
+ case 1: {
+ assertOk.push([history.scrollRestoration, "History object has scrollRestoration property."]);
+ assertIs.push([history.scrollRestoration, "auto", "history.scrollRestoration's default value should be 'auto'."]);
+ history.scrollRestoration = "foobar";
+ assertIs.push([history.scrollRestoration, "auto", "Invalid enum value should not change the value of an attribute."]);
+ history.scrollRestoration = "manual";
+ assertIs.push([history.scrollRestoration, "manual", "Valid enum value should change the value of an attribute."]);
+ history.scrollRestoration = "auto";
+ assertIs.push([history.scrollRestoration, "auto", "Valid enum value should change the value of an attribute."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs, assertOk});
+ document.getElementById("bottom").scrollIntoView();
+ window.location.reload(false);
+ break;
+ }
+ case 2: {
+ assertIsNot.push([Math.round(window.scrollY), 0, "Should have restored scrolling."]);
+ assertIs.push([history.scrollRestoration, "auto", "Should have the same scrollRestoration as before reload."]);
+ history.scrollRestoration = "manual";
+ bc.postMessage({command: "asserts", currentCase, assertIs, assertIsNot});
+ window.location.reload(false);
+ break;
+ }
+ case 3: {
+ assertIs.push([window.scrollY, 0, "Should not have restored scrolling."]);
+ assertIs.push([history.scrollRestoration, "manual", "Should have the same scrollRestoration as before reload."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs});
+ bc.close();
+ window.close();
+ break;
+ }
+ }
+ }
+ window.onpageshow = (event) => {
+ bc.postMessage({command: "pageshow", persisted: event.persisted});
+ }
+ </script>
+ </head>
+ <body>
+ <div style="border: 1px solid black; height: 5000px;">
+ &nbsp;</div>
+ <div id="bottom">Hello world</div>
+ <a href="#hash" name="hash">hash</a>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html^headers^ b/docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html^headers^
new file mode 100644
index 0000000000..f944e3806d
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store \ No newline at end of file
diff --git a/docshell/test/navigation/file_scrollRestoration_part2_bfcache.html b/docshell/test/navigation/file_scrollRestoration_part2_bfcache.html
new file mode 100644
index 0000000000..2776e42a6e
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_part2_bfcache.html
@@ -0,0 +1,57 @@
+<html>
+ <head>
+ <script>
+ var bc = new BroadcastChannel("bug1155730_part2");
+ bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "test") {
+ var currentCase = msg.currentCase;
+ test(currentCase);
+ }
+ }
+
+ function test(currentCase) {
+ var assertIs = [];
+ var assertIsNot = [];
+ switch (currentCase) {
+ case 1: {
+ history.scrollRestoration = "manual";
+ document.getElementById("bottom").scrollIntoView();
+ window.location.href = "file_scrollRestoration_navigate.html";
+ break;
+ }
+ case 2: {
+ assertIsNot.push([Math.round(window.scrollY), 0, "Should have kept the old scroll position."]);
+ assertIs.push([history.scrollRestoration, "manual", "Should have the same scrollRestoration as before reload."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs, assertIsNot, assert2: "assert2"});
+ window.scrollTo(0, 0);
+ window.location.hash = "hash";
+ bc.postMessage({command: "nextCase"});
+ requestAnimationFrame(() => {
+ test(currentCase + 1);
+ });
+ break;
+ }
+ case 3: {
+ assertIsNot.push([Math.round(window.scrollY), 0, "Should have scrolled to #hash."]);
+ assertIs.push([history.scrollRestoration, "manual", "Should have the same scrollRestoration mode as before fragment navigation."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs, assertIsNot});
+ bc.close();
+ window.close();
+ break;
+ }
+ }
+ }
+ window.onpageshow = (event) => {
+ bc.postMessage({command: "pageshow", persisted: event.persisted});
+ }
+ </script>
+ </head>
+ <body>
+ <div style="border: 1px solid black; height: 5000px;">
+ &nbsp;</div>
+ <div id="bottom">Hello world</div>
+ <a href="#hash" name="hash">hash</a>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html b/docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html
new file mode 100644
index 0000000000..ffc68d6ccc
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html
@@ -0,0 +1,157 @@
+<html>
+ <head>
+ <script>
+ var oldHistoryObject = null;
+ var currCaseForIframe = 0;
+ var bc = new BroadcastChannel("bug1155730_part3");
+ bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "test") {
+ var currentCase = msg.currentCase;
+ test(currentCase);
+ }
+ }
+
+ // If onpopstate event takes place, check if we need to call 'test()'
+ var callTest = false;
+ var nextCase = 0;
+ window.onpopstate = (e) => {
+ if (callTest) {
+ callTest = false;
+ setTimeout(() => {
+ test(nextCase);
+ });
+ }
+ }
+
+ function test(currentCase) {
+ var assertIs = [];
+ var assertOk = [];
+ var assertIsNot = [];
+ switch (currentCase) {
+ case 1: {
+ history.scrollRestoration = "manual";
+ window.location.hash = "hash";
+ bc.postMessage({command: "nextCase"});
+ requestAnimationFrame(() => {
+ test(currentCase + 1);
+ });
+ break;
+ }
+ case 2: {
+ assertIsNot.push([Math.round(window.scrollY), 0, "Should have scrolled to #hash."]);
+ assertIs.push([history.scrollRestoration, "manual", "Should have the same scrollRestoration mode as before fragment navigation."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs, assertIsNot});
+ window.location.href = "file_scrollRestoration_navigate.html";
+ break;
+ }
+ case 3: {
+ assertIs.push([window.scrollY, 0, "Shouldn't have kept the old scroll position."]);
+ assertIs.push([history.scrollRestoration, "manual", "Should have the same scrollRestoration mode as before fragment navigation."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs});
+ history.scrollRestoration = "auto";
+ document.getElementById("bottom").scrollIntoView();
+ history.pushState({ state: "state1" }, "state1");
+ history.pushState({ state: "state2" }, "state2");
+ window.scrollTo(0, 0);
+ bc.postMessage({command: "nextCase"});
+ callTest = true;
+ nextCase = currentCase + 1;
+ history.back(); // go back to state 1
+ break;
+ }
+ case 4: {
+ assertIsNot.push([Math.round(window.scrollY), 0, "Should have scrolled back to the state1's position"]);
+ assertIs.push([history.state.state, "state1", "Unexpected state."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs, assertIsNot});
+
+ history.scrollRestoration = "manual";
+ document.getElementById("bottom").scrollIntoView();
+ history.pushState({ state: "state3" }, "state3");
+ history.pushState({ state: "state4" }, "state4");
+ window.scrollTo(0, 0);
+ bc.postMessage({command: "nextCase"});
+ callTest = true;
+ nextCase = currentCase + 1;
+ history.back(); // go back to state 3
+ break;
+ }
+ case 5: {
+ assertIs.push([Math.round(window.scrollY), 0, "Shouldn't have scrolled back to the state3's position"]);
+ assertIs.push([history.state.state, "state3", "Unexpected state."]);
+
+ history.pushState({ state: "state5" }, "state5");
+ history.scrollRestoration = "auto";
+ document.getElementById("bottom").scrollIntoView();
+ assertIsNot.push([Math.round(window.scrollY), 0, "Should have scrolled to 'bottom'."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs, assertIsNot});
+ bc.postMessage({command: "nextCase"});
+ callTest = true;
+ nextCase = currentCase + 1;
+ // go back to state 3 (state 4 was removed when state 5 was pushed)
+ history.back();
+ break;
+ }
+ case 6: {
+ window.scrollTo(0, 0);
+ bc.postMessage({command: "nextCase"});
+ callTest = true;
+ nextCase = currentCase + 1;
+ history.forward();
+ break;
+ }
+ case 7: {
+ assertIsNot.push([Math.round(window.scrollY), 0, "Should have scrolled back to the state5's position"]);
+ bc.postMessage({command: "asserts", currentCase, assertIsNot});
+
+ var ifr = document.createElement("iframe");
+ ifr.src = "data:text/html,";
+ document.body.appendChild(ifr);
+ bc.postMessage({command: "nextCase"});
+ currCaseForIframe = currentCase + 1;
+ ifr.onload = () => {
+ test(currCaseForIframe);
+ };
+ break;
+ }
+ case 8: {
+ oldHistoryObject = SpecialPowers.wrap(document.getElementsByTagName("iframe")[0]).contentWindow.history;
+ bc.postMessage({command: "nextCase"});
+ currCaseForIframe++;
+ document.getElementsByTagName("iframe")[0].src = "about:blank";
+ break;
+ }
+ case 9: {
+ try {
+ oldHistoryObject.scrollRestoration;
+ assertOk.push([false, "Should have thrown an exception."]);
+ } catch (ex) {
+ assertOk.push([ex != null, "Did get an exception"]);
+ }
+ try {
+ oldHistoryObject.scrollRestoration = "auto";
+ assertOk.push([false, "Should have thrown an exception."]);
+ } catch (ex) {
+ assertOk.push([ex != null, "Did get an exception"]);
+ }
+ bc.postMessage({command: "asserts", currentCase, assertOk});
+ bc.postMessage({command: "finishing"});
+ bc.close();
+ window.close();
+ break;
+ }
+ }
+ }
+ window.onpageshow = (event) => {
+ bc.postMessage({command: "pageshow", persisted: event.persisted});
+ }
+ </script>
+ </head>
+ <body>
+ <div style="border: 1px solid black; height: 5000px;">
+ &nbsp;</div>
+ <div id="bottom">Hello world</div>
+ <a href="#hash" name="hash">hash</a>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html^headers^ b/docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html^headers^
new file mode 100644
index 0000000000..f944e3806d
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store \ No newline at end of file
diff --git a/docshell/test/navigation/file_session_history_on_redirect.html b/docshell/test/navigation/file_session_history_on_redirect.html
new file mode 100644
index 0000000000..df7e99a1dd
--- /dev/null
+++ b/docshell/test/navigation/file_session_history_on_redirect.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script>
+ window.onpagehide = function(event) {
+ opener.is(event.persisted, false, "Should have disabled bfcache for this test.");
+ }
+
+ window.onpageshow = function(event) {
+ opener.pageshow();
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_session_history_on_redirect.html^headers^ b/docshell/test/navigation/file_session_history_on_redirect.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/navigation/file_session_history_on_redirect.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/navigation/file_session_history_on_redirect_2.html b/docshell/test/navigation/file_session_history_on_redirect_2.html
new file mode 100644
index 0000000000..df7e99a1dd
--- /dev/null
+++ b/docshell/test/navigation/file_session_history_on_redirect_2.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script>
+ window.onpagehide = function(event) {
+ opener.is(event.persisted, false, "Should have disabled bfcache for this test.");
+ }
+
+ window.onpageshow = function(event) {
+ opener.pageshow();
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_session_history_on_redirect_2.html^headers^ b/docshell/test/navigation/file_session_history_on_redirect_2.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/navigation/file_session_history_on_redirect_2.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/navigation/file_sessionhistory_iframe_removal.html b/docshell/test/navigation/file_sessionhistory_iframe_removal.html
new file mode 100644
index 0000000000..f18e849942
--- /dev/null
+++ b/docshell/test/navigation/file_sessionhistory_iframe_removal.html
@@ -0,0 +1,37 @@
+<html>
+ <head>
+ <script>
+ async function wait() {
+ return opener.SpecialPowers.spawnChrome([], function() { /*dummy */ });
+ }
+ async function run() {
+ var counter = 0;
+ while(true) {
+ // Push new history entries until we hit the limit and start removing
+ // entries from the front.
+ var len = history.length;
+ document.getElementById("ifr1").contentWindow.history.pushState(++counter, null, null);
+ await wait();
+ if (len == history.length) {
+ opener.ok(true, "Found max length " + len);
+ document.getElementById("ifr2").contentWindow.history.pushState(++counter, null, null);
+ await wait();
+ document.getElementById("ifr1").contentWindow.history.pushState(++counter, null, null);
+ await wait();
+ opener.is(len, history.length, "Adding session history entries in different iframe should behave the same way");
+ // This should remove all the history entries for ifr1, leaving just
+ // the ones for ifr2.
+ document.getElementById("ifr1").remove();
+ opener.is(history.length, 2, "Should have entries for the initial load and the pushState for ifr2");
+ opener.finishTest();
+ break;
+ }
+ }
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(run)">
+ <iframe src="blank.html" id="ifr1"></iframe>
+ <iframe src="blank.html" id="ifr2"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_shiftReload_and_pushState.html b/docshell/test/navigation/file_shiftReload_and_pushState.html
new file mode 100644
index 0000000000..7882143c83
--- /dev/null
+++ b/docshell/test/navigation/file_shiftReload_and_pushState.html
@@ -0,0 +1,28 @@
+<html>
+ <head>
+ <script>
+ function test() {
+ try {
+ frames[0].history.pushState({}, "state", "?pushed");
+ } catch (ex) {
+ opener.ok(false, "history.pushState shouldn't throw");
+ }
+
+ if (!opener.shiftReloadPushStateFirstRound) {
+ opener.shiftReloadPushStateFirstRound = true;
+ window.location.reload(true);
+ } else {
+ opener.ok(true, "Did run history.push");
+ opener.finishTest();
+ }
+ }
+
+ window.addEventListener("load", function() { setTimeout(test, 0); });
+ </script>
+ </head>
+ <body>
+ <iframe src="frame0.html"></iframe>
+ <script>
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_ship_beforeunload_fired.html b/docshell/test/navigation/file_ship_beforeunload_fired.html
new file mode 100644
index 0000000000..a1f416f959
--- /dev/null
+++ b/docshell/test/navigation/file_ship_beforeunload_fired.html
@@ -0,0 +1,37 @@
+<html>
+ <script>
+ onpageshow = function(pageShowEvent) {
+ var bc = new BroadcastChannel("ship_beforeunload");
+ bc.onmessage = function(event) {
+ if (event.data.action == "register_beforeunload") {
+ onbeforeunload = function() {
+ bc.postMessage("beforeunload_fired");
+ bc.close();
+ }
+ if (event.data.loadNextPageFromSessionHistory) {
+ history.back();
+ } else {
+ location.href += "?differentpage";
+ }
+ } else if (event.data.action == "navigate_to_page_b") {
+ bc.close();
+ if (event.data.blockBFCache) {
+ window.blockBFCache = new RTCPeerConnection();
+ }
+ location.href += "?pageb";
+ } else if (event.data.action == "back_to_page_b") {
+ if (event.data.forwardNavigateToPageB) {
+ history.forward();
+ } else {
+ history.back();
+ }
+ bc.close();
+ } else if (event.data.action == "close") {
+ bc.close();
+ window.close();
+ }
+ }
+ bc.postMessage({type: pageShowEvent.type, persisted: pageShowEvent.persisted});
+ }
+ </script>
+</html>
diff --git a/docshell/test/navigation/file_static_and_dynamic_1.html b/docshell/test/navigation/file_static_and_dynamic_1.html
new file mode 100644
index 0000000000..e66216c41e
--- /dev/null
+++ b/docshell/test/navigation/file_static_and_dynamic_1.html
@@ -0,0 +1,31 @@
+<html>
+ <head>
+ <script>
+ function test() {
+ var ifr = document.createElement("iframe");
+ ifr.src = "frame0.html";
+ document.getElementById("dynamic").appendChild(ifr);
+ var staticFrame = document.getElementById("staticframe");
+ staticFrame.onload = window.location = "goback.html";
+ staticFrame.contentWindow.location = "frame1.html";
+ }
+
+ function start() {
+ if (++opener.testCount == 1) {
+ test();
+ } else {
+ var staticFrame = document.getElementById("staticframe");
+ opener.ok(String(staticFrame.contentWindow.location).includes(staticFrame.src),
+ "Wrong document loaded!");
+ opener.finishTest();
+ }
+ }
+ </script>
+ </head>
+ <body onload="setTimeout('start()', 0)">
+ <h5>Dynamic</h5>
+ <div id="dynamic"></div>
+ <h5>Static</h5>
+ <div id="static"><iframe id="staticframe" src="frame0.html"></iframe></div>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_tell_opener.html b/docshell/test/navigation/file_tell_opener.html
new file mode 100644
index 0000000000..bd45c275e6
--- /dev/null
+++ b/docshell/test/navigation/file_tell_opener.html
@@ -0,0 +1,8 @@
+<html>
+ <body onload="bodyLoaded()">Frame 1</body>
+ <script>
+ function bodyLoaded() {
+ opener.postMessage("body-loaded", "*");
+ }
+ </script>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_frame_1.html b/docshell/test/navigation/file_triggeringprincipal_frame_1.html
new file mode 100644
index 0000000000..528437f892
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_frame_1.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset="utf-8"></head>
+<body>
+<b>Frame 1</b><br/>
+<a href="#"" id="testlink" onclick="parent.frames[1].frames[0].location='http://test2.mochi.test:8888/tests/docshell/test/navigation/file_triggeringprincipal_subframe_nav.html'">click me</a>
+
+<script type="application/javascript">
+ // make sure to set document.domain to the same domain as the subframe
+ window.onload = function() {
+ document.domain = "mochi.test";
+ };
+ window.addEventListener("message", receiveMessage);
+ function receiveMessage(event) {
+ // make sure to get the right start command, otherwise
+ // let the parent know and fail the test
+ if (event.data.start !== "startTest") {
+ window.removeEventListener("message", receiveMessage);
+ window.parent.postMessage({triggeringPrincipalURI: "false"}, "*");
+ }
+ // click the link to navigate the subframe
+ document.getElementById("testlink").click();
+ }
+</script>
+
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_frame_2.html b/docshell/test/navigation/file_triggeringprincipal_frame_2.html
new file mode 100644
index 0000000000..ef7cdfc178
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_frame_2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset="utf-8"></head>
+<body>
+<b>Frame 2</b><br/>
+<iframe src="http://test2.mochi.test:8888/tests/docshell/test/navigation/file_triggeringprincipal_subframe.html"></iframe>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a.html b/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a.html
new file mode 100644
index 0000000000..75b2933c1b
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+Frame A
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a_nav.html b/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a_nav.html
new file mode 100644
index 0000000000..0479f5e1e5
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a_nav.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+Frame A navigated by Frame B
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_b.html b/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_b.html
new file mode 100644
index 0000000000..e5d40b267a
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_b.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<body>
+Frame B navigating Frame A
+
+<script type="text/javascript">
+
+window.open("file_triggeringprincipal_iframe_iframe_window_open_frame_a_nav.html", "framea");
+
+</script>
+
+</body>
+</html>
+
+
diff --git a/docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_base.html b/docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_base.html
new file mode 100644
index 0000000000..caa6b275b9
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_base.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+base test frame
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_nav.html b/docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_nav.html
new file mode 100644
index 0000000000..f4a4d0e631
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_nav.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+navigated by window.open()
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_subframe.html b/docshell/test/navigation/file_triggeringprincipal_subframe.html
new file mode 100644
index 0000000000..ba6b6dc09a
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_subframe.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset='utf-8'></head>
+<body>
+<b>Sub Frame 2</b><br/>
+<script type='application/javascript'>
+ // make sure to set document.domain to same domain as frame 1
+ window.onload = function() {
+ document.domain = "mochi.test";
+ // let Frame 1 know that we are ready to run the test
+ window.parent.parent.frames[0].postMessage({start: "startTest"}, "*");
+ };
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_subframe_nav.html b/docshell/test/navigation/file_triggeringprincipal_subframe_nav.html
new file mode 100644
index 0000000000..582181c00d
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_subframe_nav.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset="utf-8"></head>
+<body onload="checkResults()">
+<b>Sub Frame 2 Navigated</b><br/>
+
+<script type='application/javascript'>
+ function checkResults() {
+ // query the uri of the loadingPrincipal and the TriggeringPrincipal and pass
+ // that information on to the parent for verification.
+ var channel = SpecialPowers.wrap(window).docShell.currentDocumentChannel;
+ var triggeringPrincipalURI = channel.loadInfo.triggeringPrincipal.asciiSpec;
+ var loadingPrincipalURI = channel.loadInfo.loadingPrincipal.asciiSpec;
+ var referrerURI = document.referrer;
+ window.parent.parent.postMessage({triggeringPrincipalURI,
+ loadingPrincipalURI,
+ referrerURI}, "*");
+ }
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_subframe_same_origin_nav.html b/docshell/test/navigation/file_triggeringprincipal_subframe_same_origin_nav.html
new file mode 100644
index 0000000000..c84e216ae8
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_subframe_same_origin_nav.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset="utf-8"></head>
+<body onload="checkResults()">
+<b>SubFrame Same-Origin Navigated</b><br/>
+
+<script type='application/javascript'>
+ function checkResults() {
+ // query the uri of the loadingPrincipal and the TriggeringPrincipal and pass
+ // that information on to the parent for verification.
+ var channel = SpecialPowers.wrap(window).docShell.currentDocumentChannel;
+ var triggeringPrincipalURI = channel.loadInfo.triggeringPrincipal.asciiSpec;
+ var loadingPrincipalURI = channel.loadInfo.loadingPrincipal.asciiSpec;
+
+ window.parent.postMessage({triggeringPrincipalURI,
+ loadingPrincipalURI}, "*");
+ }
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_window_open.html b/docshell/test/navigation/file_triggeringprincipal_window_open.html
new file mode 100644
index 0000000000..d0644a4d5c
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_window_open.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+http
+</body>
+</html>
diff --git a/docshell/test/navigation/frame0.html b/docshell/test/navigation/frame0.html
new file mode 100644
index 0000000000..93d1c9c822
--- /dev/null
+++ b/docshell/test/navigation/frame0.html
@@ -0,0 +1,3 @@
+<html>
+ <body>Frame 0</body>
+</html>
diff --git a/docshell/test/navigation/frame1.html b/docshell/test/navigation/frame1.html
new file mode 100644
index 0000000000..4d06c09d1c
--- /dev/null
+++ b/docshell/test/navigation/frame1.html
@@ -0,0 +1,3 @@
+<html>
+ <body>Frame 1</body>
+</html>
diff --git a/docshell/test/navigation/frame2.html b/docshell/test/navigation/frame2.html
new file mode 100644
index 0000000000..7a3b5e0b9b
--- /dev/null
+++ b/docshell/test/navigation/frame2.html
@@ -0,0 +1,3 @@
+<html>
+ <body>Frame 2</body>
+</html>
diff --git a/docshell/test/navigation/frame3.html b/docshell/test/navigation/frame3.html
new file mode 100644
index 0000000000..fd24293873
--- /dev/null
+++ b/docshell/test/navigation/frame3.html
@@ -0,0 +1,3 @@
+<html>
+ <body>Frame 3</body>
+</html>
diff --git a/docshell/test/navigation/frame_1_out_of_6.html b/docshell/test/navigation/frame_1_out_of_6.html
new file mode 100644
index 0000000000..93547cd1c4
--- /dev/null
+++ b/docshell/test/navigation/frame_1_out_of_6.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 1
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://test1.mochi.test:8888/tests/docshell/test/navigation/frame_2_out_of_6.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/frame_2_out_of_6.html b/docshell/test/navigation/frame_2_out_of_6.html
new file mode 100644
index 0000000000..02056acce8
--- /dev/null
+++ b/docshell/test/navigation/frame_2_out_of_6.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 2
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://sub1.test1.mochi.test:8888/tests/docshell/test/navigation/frame_3_out_of_6.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/frame_3_out_of_6.html b/docshell/test/navigation/frame_3_out_of_6.html
new file mode 100644
index 0000000000..e9dc308f6e
--- /dev/null
+++ b/docshell/test/navigation/frame_3_out_of_6.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 3
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://sub2.xn--lt-uia.mochi.test:8888/tests/docshell/test/navigation/frame_4_out_of_6.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/frame_4_out_of_6.html b/docshell/test/navigation/frame_4_out_of_6.html
new file mode 100644
index 0000000000..66a5083e4f
--- /dev/null
+++ b/docshell/test/navigation/frame_4_out_of_6.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 4
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://test2.mochi.test:8888/tests/docshell/test/navigation/frame_5_out_of_6.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/frame_5_out_of_6.html b/docshell/test/navigation/frame_5_out_of_6.html
new file mode 100644
index 0000000000..0121f0f749
--- /dev/null
+++ b/docshell/test/navigation/frame_5_out_of_6.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 5
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://example.org:80/tests/docshell/test/navigation/frame_6_out_of_6.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/frame_6_out_of_6.html b/docshell/test/navigation/frame_6_out_of_6.html
new file mode 100644
index 0000000000..c9827ccaae
--- /dev/null
+++ b/docshell/test/navigation/frame_6_out_of_6.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 6
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://example.com/tests/docshell/test/navigation/frame_1_out_of_6.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/frame_load_as_example_com.html b/docshell/test/navigation/frame_load_as_example_com.html
new file mode 100644
index 0000000000..a1a4e7110a
--- /dev/null
+++ b/docshell/test/navigation/frame_load_as_example_com.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ example.com
+ <iframe style="height: 100vh; width:100%;" id="static" src="http://example.org/tests/docshell/test/navigation/frame_load_as_example_org.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/frame_load_as_example_org.html b/docshell/test/navigation/frame_load_as_example_org.html
new file mode 100644
index 0000000000..2fbb8038c9
--- /dev/null
+++ b/docshell/test/navigation/frame_load_as_example_org.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ example.org
+ <iframe style="height: 100vh; width:100%;" id="static" src="http://example.com/tests/docshell/test/navigation/frame_load_as_example_com.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/frame_load_as_host1.html b/docshell/test/navigation/frame_load_as_host1.html
new file mode 100644
index 0000000000..eb006b21a1
--- /dev/null
+++ b/docshell/test/navigation/frame_load_as_host1.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 1
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://example.com/tests/docshell/test/navigation/frame_load_as_host2.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/frame_load_as_host2.html b/docshell/test/navigation/frame_load_as_host2.html
new file mode 100644
index 0000000000..5457c17e9b
--- /dev/null
+++ b/docshell/test/navigation/frame_load_as_host2.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 2
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://test1.mochi.test:8888/tests/docshell/test/navigation/frame_load_as_host3.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/frame_load_as_host3.html b/docshell/test/navigation/frame_load_as_host3.html
new file mode 100644
index 0000000000..a9064ec867
--- /dev/null
+++ b/docshell/test/navigation/frame_load_as_host3.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 3
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://sub1.test1.mochi.test:8888/tests/docshell/test/navigation/frame_load_as_host1.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/frame_recursive.html b/docshell/test/navigation/frame_recursive.html
new file mode 100644
index 0000000000..835d9d63a2
--- /dev/null
+++ b/docshell/test/navigation/frame_recursive.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ example.com
+ <iframe style="height: 100vh; width:100%;" id="static" src="http://example.com/tests/docshell/test/navigation/frame_recursive.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/goback.html b/docshell/test/navigation/goback.html
new file mode 100644
index 0000000000..ce2968374e
--- /dev/null
+++ b/docshell/test/navigation/goback.html
@@ -0,0 +1,5 @@
+<html>
+ <body onload="setTimeout('window.history.go(-1)', 1000);">
+ window.history.go(-1);
+ </body>
+</html>
diff --git a/docshell/test/navigation/iframe.html b/docshell/test/navigation/iframe.html
new file mode 100644
index 0000000000..f8fce53c55
--- /dev/null
+++ b/docshell/test/navigation/iframe.html
@@ -0,0 +1,9 @@
+<html>
+<body>
+<script>
+var src = window.location.hash.substring(1);
+// eslint-disable-next-line no-unsanitized/method
+document.write('<iframe src="' + src + '"></iframe>');
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/iframe_slow_onload.html b/docshell/test/navigation/iframe_slow_onload.html
new file mode 100644
index 0000000000..e8555699bb
--- /dev/null
+++ b/docshell/test/navigation/iframe_slow_onload.html
@@ -0,0 +1,5 @@
+<html>
+ <body>
+ <iframe id="inner" src="iframe_slow_onload_inner.html">
+ </body>
+</html>
diff --git a/docshell/test/navigation/iframe_slow_onload_inner.html b/docshell/test/navigation/iframe_slow_onload_inner.html
new file mode 100644
index 0000000000..ad39eba795
--- /dev/null
+++ b/docshell/test/navigation/iframe_slow_onload_inner.html
@@ -0,0 +1,19 @@
+<html>
+ <head>
+ <script>
+ function load() {
+ // Let the test page know that it can try to navigate.
+ top.postMessage("onload", "*");
+ // We're starting an infinite loop, but we need to time out after a
+ // while, or the loop will keep running until shutdown.
+ let t0 = performance.now();
+ while (performance.now() - t0 < 5000) {
+ document.getElementById("output").innerText = Math.random();
+ }
+ }
+ </script>
+ </head>
+ <body onload="load()">
+ <p id="output"></p>
+ </body>
+</html>
diff --git a/docshell/test/navigation/iframe_static.html b/docshell/test/navigation/iframe_static.html
new file mode 100644
index 0000000000..1bdd1437c1
--- /dev/null
+++ b/docshell/test/navigation/iframe_static.html
@@ -0,0 +1,8 @@
+<html>
+ <body>
+ Nested Frame
+ <div id="frameContainer">
+ <iframe id="staticFrame" src="frame0.html"></iframe>
+ </div>
+ </body>
+</html>
diff --git a/docshell/test/navigation/mochitest.ini b/docshell/test/navigation/mochitest.ini
new file mode 100644
index 0000000000..1c4fe28309
--- /dev/null
+++ b/docshell/test/navigation/mochitest.ini
@@ -0,0 +1,255 @@
+[DEFAULT]
+support-files =
+ NavigationUtils.js
+ navigation_target_url.html
+ navigation_target_popup_url.html
+ blank.html
+ file_bug386782_contenteditable.html
+ file_bug386782_designmode.html
+ redbox_bug430723.html
+ bluebox_bug430723.html
+ file_bug462076_1.html
+ file_bug462076_2.html
+ file_bug462076_3.html
+ file_bug508537_1.html
+ file_bug534178.html
+ file_document_write_1.html
+ file_fragment_handling_during_load.html
+ file_fragment_handling_during_load_frame1.html
+ file_fragment_handling_during_load_frame2.sjs
+ file_nested_frames.html
+ file_nested_frames_innerframe.html
+ file_shiftReload_and_pushState.html
+ file_static_and_dynamic_1.html
+ frame0.html
+ frame1.html
+ frame2.html
+ frame3.html
+ goback.html
+ iframe.html
+ iframe_static.html
+ navigate.html
+ open.html
+ parent.html
+ file_tell_opener.html
+ file_triggeringprincipal_frame_1.html
+ file_triggeringprincipal_frame_2.html
+ file_triggeringprincipal_subframe.html
+ file_triggeringprincipal_subframe_nav.html
+ file_triggeringprincipal_subframe_same_origin_nav.html
+ file_triggeringprincipal_window_open.html
+ file_triggeringprincipal_parent_iframe_window_open_base.html
+ file_triggeringprincipal_parent_iframe_window_open_nav.html
+ file_triggeringprincipal_iframe_iframe_window_open_frame_a.html
+ file_triggeringprincipal_iframe_iframe_window_open_frame_b.html
+ file_triggeringprincipal_iframe_iframe_window_open_frame_a_nav.html
+ file_load_history_entry_page_with_one_link.html
+ file_load_history_entry_page_with_two_links.html
+ file_bug1300461.html
+ file_bug1300461_redirect.html
+ file_bug1300461_redirect.html^headers^
+ file_bug1300461_back.html
+ file_contentpolicy_block_window.html
+ file_bug1326251.html
+ file_bug1326251_evict_cache.html
+ file_bug1364364-1.html
+ file_bug1364364-2.html
+ file_bug1375833.html
+ file_bug1375833-frame1.html
+ file_bug1375833-frame2.html
+ test_bug145971.html
+ file_bug1609475.html
+ file_bug1379762-1.html
+ file_scrollRestoration_bfcache_and_nobfcache.html
+ file_scrollRestoration_bfcache_and_nobfcache_part2.html
+ file_scrollRestoration_bfcache_and_nobfcache_part2.html^headers^
+ file_scrollRestoration_navigate.html
+ file_scrollRestoration_part1_nobfcache.html
+ file_scrollRestoration_part1_nobfcache.html^headers^
+ file_scrollRestoration_part2_bfcache.html
+ file_scrollRestoration_part3_nobfcache.html
+ file_scrollRestoration_part3_nobfcache.html^headers^
+ file_session_history_on_redirect.html
+ file_session_history_on_redirect.html^headers^
+ file_session_history_on_redirect_2.html
+ file_session_history_on_redirect_2.html^headers^
+ redirect_handlers.sjs
+ frame_load_as_example_com.html
+ frame_load_as_example_org.html
+ frame_load_as_host1.html
+ frame_load_as_host2.html
+ frame_load_as_host3.html
+ frame_1_out_of_6.html
+ frame_2_out_of_6.html
+ frame_3_out_of_6.html
+ frame_4_out_of_6.html
+ frame_5_out_of_6.html
+ frame_6_out_of_6.html
+ frame_recursive.html
+ object_recursive_load.html
+ file_nested_srcdoc.html
+
+[test_aboutblank_change_process.html]
+[test_beforeunload_and_bfcache.html]
+support-files = file_beforeunload_and_bfcache.html
+[test_bug13871.html]
+skip-if =
+ http3
+[test_bug1583110.html]
+support-files = file_bug1583110.html
+[test_bug1706090.html]
+support-files = file_bug1706090.html
+skip-if = sessionHistoryInParent # The test is currently for the old bfcache implementation
+[test_bug1745638.html]
+support-files = file_bug1745638.html
+[test_bug1747019.html]
+support-files =
+ goback.html
+ cache_control_max_age_3600.sjs
+[test_bug1750973.html]
+support-files = file_bug1750973.html
+[test_bug1758664.html]
+support-files = file_bug1758664.html
+skip-if = !sessionHistoryInParent # the old implementation behaves inconsistently
+[test_bug270414.html]
+skip-if =
+ http3
+[test_bug278916.html]
+[test_bug279495.html]
+[test_bug344861.html]
+skip-if = toolkit == "android"
+[test_bug386782.html]
+[test_bug430624.html]
+[test_bug430723.html]
+skip-if =
+ (!debug && (os == 'mac' || os == 'win')) # Bug 874423
+ http3
+[test_bug1364364.html]
+skip-if = (os == "android") # Bug 1560378
+[test_bug1375833.html]
+[test_bug1536471.html]
+support-files = file_bug1536471.html
+[test_blockBFCache.html]
+support-files =
+ file_blockBFCache.html
+ slow.sjs
+ iframe_slow_onload.html
+ iframe_slow_onload_inner.html
+skip-if =
+ http3
+[test_child.html]
+[test_docshell_gotoindex.html]
+support-files = file_docshell_gotoindex.html
+[test_evict_from_bfcache.html]
+support-files = file_evict_from_bfcache.html
+[test_grandchild.html]
+skip-if =
+ http3
+[test_load_history_entry.html]
+[test_meta_refresh.html]
+support-files = file_meta_refresh.html
+[test_navigation_type.html]
+support-files = file_navigation_type.html
+[test_new_shentry_during_history_navigation.html]
+support-files =
+ file_new_shentry_during_history_navigation_1.html
+ file_new_shentry_during_history_navigation_1.html^headers^
+ file_new_shentry_during_history_navigation_2.html
+ file_new_shentry_during_history_navigation_2.html^headers^
+ file_new_shentry_during_history_navigation_3.html
+ file_new_shentry_during_history_navigation_3.html^headers^
+ file_new_shentry_during_history_navigation_4.html
+[test_not-opener.html]
+skip-if =
+ http3
+[test_online_offline_bfcache.html]
+support-files = file_online_offline_bfcache.html
+[test_opener.html]
+skip-if =
+ fission && xorigin # Bug 1716402 - New fission platform triage
+ os == "linux" && bits == 64 # Bug 1572299
+ win10_2004 # Bug 1572299
+ win11_2009 # Bug 1797751
+ fission && os == "android" # Bug 1827323
+[test_popup-navigates-children.html]
+skip-if =
+ http3
+[test_reload.html]
+support-files = file_reload.html
+[test_reload_nonbfcached_srcdoc.html]
+support-files = file_reload_nonbfcached_srcdoc.sjs
+skip-if =
+ http3
+[test_reserved.html]
+skip-if =
+ debug # bug 1263213
+[test_performance_navigation.html]
+[test_same_url.html]
+support-files = file_same_url.html
+[test_session_history_on_redirect.html]
+[test_sessionhistory.html]
+skip-if = verify && (os == 'mac') && debug # Hit MOZ_CRASH(Shutdown too long, probably frozen, causing a crash.) bug 1677545
+[test_dynamic_frame_forward_back.html]
+[test_sessionhistory_document_write.html]
+[test_sessionhistory_iframe_removal.html]
+support-files = file_sessionhistory_iframe_removal.html
+[test_session_history_entry_cleanup.html]
+[test_fragment_handling_during_load.html]
+[test_nested_frames.html]
+skip-if =
+ http3
+[test_shiftReload_and_pushState.html]
+[test_scrollRestoration.html]
+[test_bug1609475.html]
+[test_bug1300461.html]
+[test_bug1326251.html]
+skip-if = toolkit == 'android' || sessionHistoryInParent # It relies on the bfcache
+[test_bug1379762.html]
+[test_state_size.html]
+[test_static_and_dynamic.html]
+skip-if = true # This was disabled for a few years now anyway, bug 1677544
+[test_sibling-matching-parent.html]
+[test_sibling-off-domain.html]
+skip-if =
+ http3
+[test_triggeringprincipal_frame_nav.html]
+skip-if =
+ http3
+[test_triggeringprincipal_frame_same_origin_nav.html]
+skip-if =
+ http3
+[test_triggeringprincipal_window_open.html]
+skip-if =
+ http3
+[test_triggeringprincipal_parent_iframe_window_open.html]
+skip-if =
+ http3
+[test_triggeringprincipal_iframe_iframe_window_open.html]
+skip-if =
+ http3
+[test_contentpolicy_block_window.html]
+[test_rate_limit_location_change.html]
+[test_reload_large_postdata.html]
+support-files =
+ file_reload_large_postdata.sjs
+[test_recursive_frames.html]
+skip-if =
+ http3
+ fission && os == "android" # Bug 1714698
+[test_bug1699721.html]
+skip-if = !fission # tests fission-only process switching behaviour
+[test_ship_beforeunload_fired.html]
+support-files =
+ file_ship_beforeunload_fired.html
+skip-if =
+ !sessionHistoryInParent
+ http3
+[test_ship_beforeunload_fired_2.html]
+support-files =
+ file_ship_beforeunload_fired.html
+skip-if = !sessionHistoryInParent
+[test_ship_beforeunload_fired_3.html]
+support-files =
+ file_ship_beforeunload_fired.html
+skip-if = !sessionHistoryInParent
+[test_open_javascript_noopener.html]
diff --git a/docshell/test/navigation/navigate.html b/docshell/test/navigation/navigate.html
new file mode 100644
index 0000000000..f68123188e
--- /dev/null
+++ b/docshell/test/navigation/navigate.html
@@ -0,0 +1,38 @@
+<html>
+<head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="NavigationUtils.js"></script>
+ <script>
+ function navigate() {
+ var args = window.location.hash.substring(1).split(",");
+ var target = args[0];
+ var mechanism = args[1];
+
+ switch (mechanism) {
+ case "location":
+ // eslint-disable-next-line no-eval
+ navigateByLocation(eval(target));
+ break;
+ case "open":
+ navigateByOpen(target);
+ break;
+ case "form":
+ navigateByForm(target);
+ break;
+ case "hyperlink":
+ navigateByHyperlink(target);
+ break;
+ }
+ }
+ </script>
+</head>
+<body onload="navigate();">
+<script>
+var args = window.location.hash.substring(1).split(",");
+var target = args[0];
+var mechanism = args[1];
+// eslint-disable-next-line no-unsanitized/method
+document.write("target=" + target + " mechanism=" + mechanism);
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/navigation_target_popup_url.html b/docshell/test/navigation/navigation_target_popup_url.html
new file mode 100644
index 0000000000..cfe6de009d
--- /dev/null
+++ b/docshell/test/navigation/navigation_target_popup_url.html
@@ -0,0 +1 @@
+<html><body>This is a popup</body></html>
diff --git a/docshell/test/navigation/navigation_target_url.html b/docshell/test/navigation/navigation_target_url.html
new file mode 100644
index 0000000000..a485e8133f
--- /dev/null
+++ b/docshell/test/navigation/navigation_target_url.html
@@ -0,0 +1 @@
+<html><body>This frame was navigated.</body></html>
diff --git a/docshell/test/navigation/object_recursive_load.html b/docshell/test/navigation/object_recursive_load.html
new file mode 100644
index 0000000000..3ae9521e63
--- /dev/null
+++ b/docshell/test/navigation/object_recursive_load.html
@@ -0,0 +1,6 @@
+<html>
+ <body width="400" height="300">
+ Frame 0
+ <object id="static" width="400" height="300" data="http://sub2.xn--lt-uia.mochi.test:8888/tests/docshell/test/navigation/object_recursive_load.html"></object>
+ </body>
+</html>
diff --git a/docshell/test/navigation/open.html b/docshell/test/navigation/open.html
new file mode 100644
index 0000000000..9a96a8dda7
--- /dev/null
+++ b/docshell/test/navigation/open.html
@@ -0,0 +1,10 @@
+<html>
+<body>
+<script>
+var target = window.location.hash.substring(1);
+// eslint-disable-next-line no-unsanitized/method
+document.write("target=" + target);
+window.open("navigation_target_popup_url.html", target, "width=10,height=10");
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/parent.html b/docshell/test/navigation/parent.html
new file mode 100644
index 0000000000..74722b8bdf
--- /dev/null
+++ b/docshell/test/navigation/parent.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<body>
+This document contains a frame.
+<div><iframe src="blank.html"></iframe></div>
+<script>
+frames[0].name = window.name + "_child0";
+window.onload = function() {
+ opener.postMessage("ready", "*");
+};
+</script>
+</body>
+</html>
+
diff --git a/docshell/test/navigation/redbox_bug430723.html b/docshell/test/navigation/redbox_bug430723.html
new file mode 100644
index 0000000000..c2d1f98092
--- /dev/null
+++ b/docshell/test/navigation/redbox_bug430723.html
@@ -0,0 +1,6 @@
+<html><head>
+<script> window.addEventListener("pageshow", function() { opener.nextTest(); }); </script>
+</head><body>
+<div style="position:absolute; left:0px; top:0px; width:50%; height:150%; background-color:red">
+<p>This is a very tall red box.</p>
+</div></body></html>
diff --git a/docshell/test/navigation/redirect_handlers.sjs b/docshell/test/navigation/redirect_handlers.sjs
new file mode 100644
index 0000000000..c2b39e61c9
--- /dev/null
+++ b/docshell/test/navigation/redirect_handlers.sjs
@@ -0,0 +1,29 @@
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Cache-Control", "no-store", false);
+
+ let state = getState("sessionhistory_do_redirect");
+ if (state != "doredirect") {
+ response.setHeader("Content-Type", "text/html");
+ const contents = `
+ <script>
+ window.onpageshow = function(event) {
+ opener.pageshow();
+ }
+ </script>
+ `;
+ response.write(contents);
+
+ // The next load should do a redirect.
+ setState("sessionhistory_do_redirect", "doredirect");
+ } else {
+ setState("sessionhistory_do_redirect", "");
+
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader(
+ "Location",
+ "file_session_history_on_redirect_2.html",
+ false
+ );
+ }
+}
diff --git a/docshell/test/navigation/redirect_to_blank.sjs b/docshell/test/navigation/redirect_to_blank.sjs
new file mode 100644
index 0000000000..b1668401ea
--- /dev/null
+++ b/docshell/test/navigation/redirect_to_blank.sjs
@@ -0,0 +1,6 @@
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Cache-Control", "no-store", false);
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", "blank.html", false);
+}
diff --git a/docshell/test/navigation/slow.sjs b/docshell/test/navigation/slow.sjs
new file mode 100644
index 0000000000..9720f807f6
--- /dev/null
+++ b/docshell/test/navigation/slow.sjs
@@ -0,0 +1,16 @@
+function handleRequest(request, response) {
+ response.processAsync();
+
+ let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.init(
+ function () {
+ response.finish();
+ },
+ 5000,
+ Ci.nsITimer.TYPE_ONE_SHOT
+ );
+
+ response.setStatusLine(null, 200, "OK");
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write("Start of the content.");
+}
diff --git a/docshell/test/navigation/test_aboutblank_change_process.html b/docshell/test/navigation/test_aboutblank_change_process.html
new file mode 100644
index 0000000000..61325570f3
--- /dev/null
+++ b/docshell/test/navigation/test_aboutblank_change_process.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+</head>
+<script>
+// Open a window and navigate it from https://example.com to about:blank
+// With fission, we should switch processes and about:blank should load in
+// the same process as this test page.
+// This is a crash test.
+add_task(async function test_aboutblank_change_process() {
+ let exampleLoaded = new Promise(resolve => {
+ function onMessage(event) {
+ if (event.data == "body-loaded") {
+ window.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }
+ window.addEventListener("message", onMessage);
+ });
+ let win = window.open();
+ win.location = "https://example.com/tests/docshell/test/navigation/file_tell_opener.html";
+ await exampleLoaded;
+
+ win.location = "about:blank";
+
+ // A crash happens somewhere here when about:blank does not go via
+ // DocumentChannel with fission enabled
+
+ // Wait for about:blank to load in this process
+ await SimpleTest.promiseWaitForCondition(() => {
+ try {
+ return win.location.href == "about:blank";
+ } catch (e) {
+ // While the `win` still has example.com page loaded, `win.location` will
+ // be a cross origin object and querying win.location.href will throw a
+ // SecurityError. Return false as long as this is the case.
+ return false;
+ }
+ })
+
+ ok(true, "We did not crash");
+ win.close();
+});
+</script>
diff --git a/docshell/test/navigation/test_beforeunload_and_bfcache.html b/docshell/test/navigation/test_beforeunload_and_bfcache.html
new file mode 100644
index 0000000000..6bb958c6c6
--- /dev/null
+++ b/docshell/test/navigation/test_beforeunload_and_bfcache.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Loading a page from BFCache and firing beforeunload on the current page</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ /*
+ * This is a simple test to ensure beforeunload is fired on the current page
+ * when restoring a page from bfcache.
+ * (1) The controller page opens a new window. Another page is loaded there
+ * and session history is navigated back to check whether bfcache is
+ * enabled. If not, close message is sent and the opened window closes
+ * and the test ends.
+ * (2) beforeunload event listener is added to the page and history.forward()
+ * is called. The event listener should send a message to the controller
+ * page.
+ * (3) Close message is sent to close the opened window and the test finishes.
+ */
+
+ var pageshowCount = 0;
+ var gotBeforeUnload = false;
+ var bfcacheDisabled = false;
+
+
+ function executeTest() {
+ var bc = new BroadcastChannel("beforeunload_and_bfcache");
+ bc.onmessage = function(event) {
+ if (event.data.type == "pageshow") {
+ ++pageshowCount;
+ if (pageshowCount == 1) {
+ bc.postMessage("nextpage");
+ } else if (pageshowCount == 2) {
+ bc.postMessage("back");
+ } else if (pageshowCount == 3) {
+ if (!event.data.persisted) {
+ ok(true, "BFCache not enabled");
+ bfcacheDisabled = true;
+ bc.postMessage("close");
+ return;
+ }
+ bc.postMessage("forward");
+ } else if (pageshowCount == 4) {
+ ok(event.data.persisted, "Should have loaded a page from bfcache.");
+ bc.postMessage("close");
+ }
+ } else if (event.data == "beforeunload") {
+ gotBeforeUnload = true;
+ } else if (event.data == "closed") {
+ isnot(bfcacheDisabled, gotBeforeUnload,
+ "Either BFCache shouldn't be enabled or a beforeunload event should have been fired.");
+ bc.close();
+ SimpleTest.finish();
+ }
+ };
+
+ function runTest() {
+ SpecialPowers.pushPrefEnv({"set": [["docshell.shistory.bfcache.allow_unload_listeners", false]]},
+ function() {
+ window.open("file_beforeunload_and_bfcache.html", "", "noopener");
+ }
+ );
+ }
+ runTest();
+ }
+
+ if (isXOrigin) {
+ // Bug 1746646: Make mochitests work with TCP enabled (cookieBehavior = 5)
+ // Acquire storage access permission here so that the BroadcastChannel used to
+ // communicate with the opened windows works in xorigin tests. Otherwise,
+ // the iframe containing this page is isolated from first-party storage access,
+ // which isolates BroadcastChannel communication.
+ SpecialPowers.wrap(document).notifyUserGestureActivation();
+ SpecialPowers.addPermission("storageAccessAPI", true, window.location.href).then(() => {
+ SpecialPowers.wrap(document).requestStorageAccess().then(() => {
+ SpecialPowers.pushPrefEnv({
+ set: [["privacy.partition.always_partition_third_party_non_cookie_storage", false]]
+ }).then(() => {
+ executeTest();
+ });
+ });
+ });
+ } else {
+ executeTest();
+ }
+
+ </script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_blockBFCache.html b/docshell/test/navigation/test_blockBFCache.html
new file mode 100644
index 0000000000..3d4a418369
--- /dev/null
+++ b/docshell/test/navigation/test_blockBFCache.html
@@ -0,0 +1,294 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Blocking pages from entering BFCache</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="">
+<script>
+
+const getUserMediaPrefs = {
+ set: [
+ ["media.devices.insecure.enabled", true],
+ ["media.getusermedia.insecure.enabled", true],
+ ["media.navigator.permission.disabled", true],
+ ],
+};
+const msePrefs = {
+ set: [
+ ["media.mediasource.enabled", true],
+ ["media.audio-max-decode-error", 0],
+ ["media.video-max-decode-error", 0],
+ ]
+};
+
+const blockBFCacheTests = [
+ {
+ name: "Request",
+ test: () => {
+ return new Promise((resolve) => {
+ const xhr = new XMLHttpRequest();
+ xhr.open("GET", "slow.sjs");
+ xhr.addEventListener("progress", () => { resolve(xhr); }, { once: true });
+ xhr.send();
+ });
+ },
+ },
+ {
+ name: "Background request",
+ test: () => {
+ return new Promise((resolve) => {
+ const xhr = new XMLHttpRequest();
+ xhr.open("GET", "slow.sjs");
+ xhr.addEventListener("readystatechange", () => { if (xhr.readyState == xhr.HEADERS_RECEIVED) resolve(xhr); });
+ xhr.send();
+ });
+ },
+ },
+ {
+ name: "getUserMedia",
+ prefs: getUserMediaPrefs,
+ test: () => {
+ return navigator.mediaDevices.getUserMedia({ audio: true, fake: true });
+ },
+ },
+ {
+ name: "RTCPeerConnection",
+ test: () => {
+ let pc = new RTCPeerConnection();
+ return pc.createOffer();
+ },
+ },
+ {
+ name: "MSE",
+ prefs: msePrefs,
+ test: () => {
+ const ms = new MediaSource();
+ const el = document.createElement("video");
+ el.src = URL.createObjectURL(ms);
+ el.preload = "auto";
+ return el;
+ },
+ },
+ {
+ name: "WebSpeech",
+ test: () => {
+ return new Promise((resolve) => {
+ const utterance = new SpeechSynthesisUtterance('bfcache');
+ utterance.lang = 'it-IT-noend';
+ utterance.addEventListener('start', () => { resolve(utterance); })
+ speechSynthesis.speak(utterance);
+ });
+ },
+ },
+ {
+ name: "WebVR",
+ prefs: {
+ set: [
+ ["dom.vr.test.enabled", true],
+ ["dom.vr.puppet.enabled", true],
+ ["dom.vr.require-gesture", false],
+ ],
+ },
+ test: () => {
+ return navigator.requestVRServiceTest();
+ }
+ },
+];
+
+if (SpecialPowers.Services.appinfo.fissionAutostart) {
+ blockBFCacheTests.push({
+ name: "Loading OOP iframe",
+ test: () => {
+ return new Promise((resolve) => {
+ const el = document.body.appendChild(document.createElement("iframe"));
+ el.id = "frame";
+ addEventListener("message", ({ data }) => {
+ if (data == "onload") {
+ resolve();
+ }
+ });
+ el.src = "https://example.com/tests/docshell/test/navigation/iframe_slow_onload.html";
+ });
+ },
+ waitForDone: () => {
+ SimpleTest.requestFlakyTimeout("Test has a loop in an onload handler that runs for 5000ms, we need to make sure the loop is done before moving to the next test.");
+ return new Promise(resolve => {
+ setTimeout(resolve, 5000);
+ });
+ },
+ });
+}
+
+const dontBlockBFCacheTests = [
+ {
+ name: "getUserMedia",
+ prefs: getUserMediaPrefs,
+ test: () => {
+ return navigator.mediaDevices.getUserMedia({ video: true, fake: true }).then(stream => {
+ stream.getTracks().forEach(track => track.stop());
+ return stream;
+ });
+ },
+ },
+/*
+ Disabled because MediaKeys rely on being destroyed by the CC before they
+ notify their window, so the test would intermittently fail depending on
+ when the CC runs.
+
+ {
+ name: "MSE",
+ prefs: msePrefs,
+ test: () => {
+ return new Promise((resolve) => {
+ const ms = new MediaSource();
+ const el = document.createElement("video");
+ ms.addEventListener("sourceopen", () => { resolve(el) }, { once: true });
+ el.src = URL.createObjectURL(ms);
+ el.preload = "auto";
+ }).then(el => {
+ el.src = "";
+ return el;
+ });
+ },
+ },
+*/
+];
+
+
+
+function executeTest() {
+
+ let bc = new BroadcastChannel("bfcache_blocking");
+
+ function promiseMessage(type) {
+ return new Promise((resolve, reject) => {
+ bc.addEventListener("message", (e) => {
+ if (e.data.type == type) {
+ resolve(e.data);
+ }
+ }, { once: true });
+ });
+ }
+
+ function promisePageShow(shouldBePersisted) {
+ return promiseMessage("pageshow").then(data => data.persisted == shouldBePersisted);
+ }
+
+ function promisePageShowFromBFCache(e) {
+ return promisePageShow(true);
+ }
+
+ function promisePageShowNotFromBFCache(e) {
+ return promisePageShow(false);
+ }
+
+ function runTests(testArray, shouldBlockBFCache) {
+ for (const { name, prefs = {}, test, waitForDone } of testArray) {
+ add_task(async function() {
+ await SpecialPowers.pushPrefEnv(prefs, async function() {
+ // Load a mostly blank page that we can communicate with over
+ // BroadcastChannel (though it will close the BroadcastChannel after
+ // receiving the next "load" message, to avoid blocking BFCache).
+ let loaded = promisePageShowNotFromBFCache();
+ window.open("file_blockBFCache.html", "", "noopener");
+ await loaded;
+
+ // Load the same page with a different URL.
+ loaded = promisePageShowNotFromBFCache();
+ bc.postMessage({ message: "load", url: `file_blockBFCache.html?${name}_${shouldBlockBFCache}` });
+ await loaded;
+
+ // Run test script in the second page.
+ bc.postMessage({ message: "runScript", fun: test.toString() });
+ await promiseMessage("runScriptDone");
+
+ // Go back to the first page (this should just come from the BFCache).
+ let goneBack = promisePageShowFromBFCache();
+ bc.postMessage({ message: "back" });
+ await goneBack;
+
+ // Go forward again to the second page and check that it does/doesn't come
+ // from the BFCache.
+ let goneForward = promisePageShow(!shouldBlockBFCache);
+ bc.postMessage({ message: "forward" });
+ let result = await goneForward;
+ ok(result, `Page ${shouldBlockBFCache ? "should" : "should not"} have been blocked from going into the BFCache (${name})`);
+
+ // If the test will keep running after navigation, then we need to make
+ // sure it's completely done before moving to the next test, to avoid
+ // interfering with any following tests. If waitForDone is defined then
+ // it'll return a Promise that we can use to wait for the end of the
+ // test.
+ if (waitForDone) {
+ await waitForDone();
+ }
+
+ // Do a similar test, but replace the bfcache test page with a new page,
+ // not a page coming from the session history.
+
+ // Load the same page with a different URL.
+ loaded = promisePageShowNotFromBFCache();
+ bc.postMessage({ message: "load", url: `file_blockBFCache.html?p2_${name}_${shouldBlockBFCache}` });
+ await loaded;
+
+ // Run the test script.
+ bc.postMessage({ message: "runScript", fun: test.toString() });
+ await promiseMessage("runScriptDone");
+
+ // Load a new page.
+ loaded = promisePageShowNotFromBFCache();
+ bc.postMessage({ message: "load", url: "file_blockBFCache.html" });
+ await loaded;
+
+ // Go back to the previous page and check that it does/doesn't come
+ // from the BFCache.
+ goneBack = promisePageShow(!shouldBlockBFCache);
+ bc.postMessage({ message: "back" });
+ result = await goneBack;
+ ok(result, `Page ${shouldBlockBFCache ? "should" : "should not"} have been blocked from going into the BFCache (${name})`);
+
+ if (waitForDone) {
+ await waitForDone();
+ }
+
+ bc.postMessage({ message: "close" });
+
+ SpecialPowers.popPrefEnv();
+ });
+ });
+ }
+ }
+
+ // If Fission is disabled, the pref is no-op.
+ SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ runTests(blockBFCacheTests, true);
+ runTests(dontBlockBFCacheTests, false);
+ });
+}
+
+if (isXOrigin) {
+ // Bug 1746646: Make mochitests work with TCP enabled (cookieBehavior = 5)
+ // Acquire storage access permission here so that the BroadcastChannel used to
+ // communicate with the opened windows works in xorigin tests. Otherwise,
+ // the iframe containing this page is isolated from first-party storage access,
+ // which isolates BroadcastChannel communication.
+ SpecialPowers.wrap(document).notifyUserGestureActivation();
+ SpecialPowers.addPermission("storageAccessAPI", true, window.location.href).then(() => {
+ SpecialPowers.wrap(document).requestStorageAccess().then(() => {
+ SpecialPowers.pushPrefEnv({
+ set: [["privacy.partition.always_partition_third_party_non_cookie_storage", false]]
+ }).then(() => {
+ executeTest();
+ });
+ });
+ });
+} else {
+ executeTest();
+}
+
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1300461.html b/docshell/test/navigation/test_bug1300461.html
new file mode 100644
index 0000000000..22783c07c2
--- /dev/null
+++ b/docshell/test/navigation/test_bug1300461.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 1300461</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1300461">Mozilla Bug 1300461</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+
+ let chromeScript = null;
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ chromeScript = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ function doSend(message, fn) {
+ try {
+ sendAsyncMessage(message, {success: true, value: fn()});
+ } catch(_) {
+ sendAsyncMessage(message, {success: false});
+ }
+ }
+
+ addMessageListener("requestedIndex", (id) => {
+ doSend("requestedIndex", () => {
+ let shistory = BrowsingContext.get(id).top.sessionHistory;
+ return shistory.requestedIndex;
+ })
+ });
+ });
+ }
+
+ async function getSHRequestedIndex(browsingContextId) {
+ let p = chromeScript.promiseOneMessage("requestedIndex");
+ chromeScript.sendAsyncMessage("requestedIndex", browsingContextId);
+ let result = await p;
+ ok(result.success, "Got requested index from parent");
+ return result.value;
+ }
+
+ var testCount = 0;
+
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_bug1300461.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ if (chromeScript) {
+ chromeScript.destroy();
+ }
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1326251.html b/docshell/test/navigation/test_bug1326251.html
new file mode 100644
index 0000000000..3c951729e6
--- /dev/null
+++ b/docshell/test/navigation/test_bug1326251.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 1326251</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1326251">Mozilla Bug 1326251</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+
+ var testCount = 0;
+
+ var bc = new BroadcastChannel("file_bug1326251");
+ bc.onmessage = function(event) {
+ if (event.data == "requestNextTest") {
+ bc.postMessage({ nextTest: testCount++ });
+ } else if (event.data.type == "is") {
+ is(event.data.value1, event.data.value2, event.data.message);
+ } else if (event.data.type == "ok") {
+ ok(event.data.value, event.data.message);
+ } else if (event.data == "finishTest") {
+ SimpleTest.finish();
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ // If Fission is disabled, the pref is no-op.
+ SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ window.open("file_bug1326251.html", "", "width=360,height=480,noopener");
+ });
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1364364.html b/docshell/test/navigation/test_bug1364364.html
new file mode 100644
index 0000000000..90d2009df4
--- /dev/null
+++ b/docshell/test/navigation/test_bug1364364.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1364364
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1364364</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1364364 */
+ let testWin, testDoc;
+ async function test() {
+ SimpleTest.waitForExplicitFinish();
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ // This test relies on the possibility to modify the bfcached document.
+ // The new implementation is more restricted and thus works only
+ // when the bfcached browsing context is the only top level one
+ // in the browsing context group.
+ ok(true, "This test is for the old bfcache implementation only.");
+ SimpleTest.finish();
+ return;
+ }
+ testWin = window.open("file_bug1364364-1.html");
+ await waitForLoad(testWin);
+ testDoc = testWin.document;
+
+ // file_bug1364364-1.html will load a few dynamic iframes and then navigate
+ // top browsing context to file_bug1364364-2.html, which will postMessage
+ // back.
+ }
+
+ function waitForLoad(win) {
+ return new Promise(r => win.addEventListener("load", r, { once: true}));
+ }
+
+ window.addEventListener("message", async function(msg) {
+ if (msg.data == "navigation-done") {
+ is(testWin.history.length, 6, "check history.length");
+
+ // Modify a document in bfcache should cause the cache being dropped tho
+ // RemoveFromBFCacheAsync.
+ testDoc.querySelector("#content").textContent = "modified";
+ await new Promise(r => setTimeout(r, 0));
+
+ is(testWin.history.length, 2, "check history.length after bfcache dropped");
+ testWin.close();
+ SimpleTest.finish();
+ }
+ });
+
+ </script>
+</head>
+<body onload="test();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1364364">Mozilla Bug 1364364</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1375833.html b/docshell/test/navigation/test_bug1375833.html
new file mode 100644
index 0000000000..c2a7750a4e
--- /dev/null
+++ b/docshell/test/navigation/test_bug1375833.html
@@ -0,0 +1,131 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1375833
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1375833</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ SimpleTest.waitForExplicitFinish();
+
+ /**
+ * Test for Bug 1375833. It tests for 2 things in a normal reload -
+ * 1. Static frame history should not be dropped.
+ * 2. In a reload, docshell would parse the reloaded root document and
+ * genearate new child docshells, and then use the child offset
+ */
+
+ let testWin = window.open("file_bug1375833.html");
+ let count = 0;
+ let webNav, shistory;
+ let frameDocShellId;
+ let chromeScript = null;
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ chromeScript = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ function doSend(message, fn) {
+ try {
+ sendAsyncMessage(message, {success: true, value: fn()});
+ } catch(_) {
+ sendAsyncMessage(message, {success: false});
+ }
+ }
+
+ addMessageListener("test1", id => {
+ doSend("test1", () => {
+ let sessionHistory = BrowsingContext.get(id).top.sessionHistory;
+ let entry = sessionHistory.getEntryAtIndex(sessionHistory.index);
+ let frameEntry = entry.GetChildAt(0);
+ return String(frameEntry.docshellID);
+ })
+ });
+ });
+ }
+
+ window.addEventListener("message", async e => {
+ switch (count++) {
+ case 0:
+ ok(e.data.endsWith("file_bug1375833-frame2.html"), "check location");
+
+ webNav = SpecialPowers.wrap(testWin)
+ .docShell
+ .QueryInterface(SpecialPowers.Ci.nsIWebNavigation);
+ shistory = webNav.sessionHistory;
+ is(shistory.count, 2, "check history length");
+ is(shistory.index, 1, "check history index");
+
+ frameDocShellId = String(getFrameDocShell().historyID);
+ ok(frameDocShellId, "sanity check for docshell ID");
+
+ testWin.location.reload();
+ break;
+ case 1:
+ ok(e.data.endsWith("file_bug1375833-frame2.html"), "check location");
+ is(shistory.count, 4, "check history length");
+ is(shistory.index, 3, "check history index");
+
+ let newFrameDocShellId = String(getFrameDocShell().historyID);
+ ok(newFrameDocShellId, "sanity check for docshell ID");
+ is(newFrameDocShellId, frameDocShellId, "check docshell ID remains after reload");
+
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ let entry = shistory.legacySHistory.getEntryAtIndex(shistory.index);
+ let frameEntry = entry.GetChildAt(0);
+ is(String(frameEntry.docshellID), frameDocShellId, "check newly added shentry uses the same docshell ID");
+ } else {
+ let p = chromeScript.promiseOneMessage("test1");
+ chromeScript.sendAsyncMessage("test1", SpecialPowers.wrap(testWin).browsingContext.id);
+ let result = await p;
+ ok(result.success, "legacySHistory worked around ok");
+ is(result.value, frameDocShellId, "check newly added shentry uses the same docshell ID");
+ }
+
+ webNav.goBack();
+ break;
+ case 2:
+ ok(e.data.endsWith("file_bug1375833-frame1.html"), "check location");
+ is(shistory.count, 4, "check history length");
+ is(shistory.index, 2, "check history index");
+
+ webNav.goBack();
+ break;
+ case 3:
+ ok(e.data.endsWith("file_bug1375833-frame2.html"), "check location");
+ is(shistory.count, 4, "check history length");
+ is(shistory.index, 1, "check history index");
+
+ webNav.goBack();
+ break;
+ case 4:
+ ok(e.data.endsWith("file_bug1375833-frame1.html"), "check location");
+ is(shistory.count, 4, "check history length");
+ is(shistory.index, 0, "check history index");
+
+ if (chromeScript) {
+ chromeScript.destroy();
+ }
+ testWin.close();
+ SimpleTest.finish();
+ }
+ });
+
+ function getFrameDocShell() {
+ return SpecialPowers.wrap(testWin.window[0]).docShell;
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1375833">Mozilla Bug 1375833</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1379762.html b/docshell/test/navigation/test_bug1379762.html
new file mode 100644
index 0000000000..eda3b539a5
--- /dev/null
+++ b/docshell/test/navigation/test_bug1379762.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 1379762</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1379762">Mozilla Bug 1379762</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ /**
+ * - This page opens new window
+ * - new window sends 'init' msg
+ * - onload() in new window sends 'increment_loadCount' msg
+ * - onpageshow() in new window sends 'increment_testCount' msg
+ * - This page sends 'forward_back' msg
+ * - onpageshow() in new window 'increment_testCount'
+ * - This page sends 'finish_test' msg
+ * - onpageshow() in new window sends 'finished' msg
+ */
+ var testCount = 0; // Used by the test files.
+ var loadCount = 0;
+ var goneBack = false;
+ var bc = new BroadcastChannel("bug1379762");
+ bc.onmessage = (messageEvent) => {
+ let message = messageEvent.data;
+ if (message == "init") {
+ is(testCount, 0, "new window should only be loaded once; otherwise the loadCount variable makes no sense");
+ } else if (message == "increment_loadCount") {
+ loadCount++;
+ is(loadCount, 1, "Should only get one load")
+ } else if (message == 'increment_testCount') {
+ testCount++;
+ if (testCount == 1) {
+ bc.postMessage("forward_back");
+ goneBack = true;
+ } else if (testCount == 2) {
+ ok(goneBack, "We had a chance to navigate backwards and forwards in the new window to test BFCache");
+ bc.postMessage("finish_test");
+ }
+ } else if (message == "finished") {
+ bc.close();
+ SimpleTest.finish();
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ // If Fission is disabled, the pref is no-op.
+ SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ window.open("file_bug1379762-1.html", "", "width=360,height=480,noopener");
+ });
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug13871.html b/docshell/test/navigation/test_bug13871.html
new file mode 100644
index 0000000000..0532bc7b56
--- /dev/null
+++ b/docshell/test/navigation/test_bug13871.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+async function runTest() {
+ navigateByLocation(window0.frames[0]);
+ navigateByOpen("window1_child0");
+ navigateByForm("window2_child0");
+ navigateByHyperlink("window3_child0");
+
+ await waitForFinishedFrames(4);
+
+ isInaccessible(window0.frames[0], "Should not be able to navigate off-domain frame by setting location.");
+ isInaccessible(window1.frames[0], "Should not be able to navigate off-domain frame by calling window.open.");
+ isInaccessible(window2.frames[0], "Should not be able to navigate off-domain frame by submitting form.");
+ isInaccessible(window3.frames[0], "Should not be able to navigate off-domain frame by targeted hyperlink.");
+
+ window0.close();
+ window1.close();
+ window2.close();
+ window3.close();
+
+ await cleanupWindows();
+ SimpleTest.finish();
+}
+
+// Because our open()'d windows are cross-origin, we can't wait for onload.
+// We instead wait for a postMessage from parent.html.
+var windows = new Map();
+addEventListener("message", function windowLoaded(evt) {
+ // Because window.open spins the event loop in order to open new windows,
+ // we might receive the "ready" message before we call waitForLoad.
+ // In that case, windows won't contain evt.source and we just note that the
+ // window is ready. Otherwise, windows contains the "resolve" function for
+ // that window's promise and we just have to call it.
+ if (windows.has(evt.source)) {
+ windows.get(evt.source)();
+ } else {
+ windows.set(evt.source, true);
+ }
+});
+
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window0 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/parent.html", "window0", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window1 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/parent.html", "window1", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window2 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/parent.html", "window2", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window3 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/parent.html", "window3", "width=10,height=10");
+
+function waitForLoad(w) {
+ return new Promise(function(resolve, reject) {
+ // If we already got the "ready" message, resolve immediately.
+ if (windows.has(w)) {
+ resolve();
+ } else {
+ windows.set(w, resolve);
+ }
+ });
+}
+
+Promise.all([ waitForLoad(window0),
+ waitForLoad(window1),
+ waitForLoad(window2),
+ waitForLoad(window3) ])
+ .then(runTest);
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=13871">Mozilla Bug 13871</a>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug145971.html b/docshell/test/navigation/test_bug145971.html
new file mode 100644
index 0000000000..ffad27a9c3
--- /dev/null
+++ b/docshell/test/navigation/test_bug145971.html
@@ -0,0 +1,29 @@
+<html>
+ <head>
+ <script>
+ let pass = false;
+ let initialLoad = false;
+ var bc = new BroadcastChannel("bug145971");
+ function checkNavigationTypeEquals2() {
+ if (performance.navigation.type == 2) {
+ pass = true;
+ }
+ testDone();
+ }
+
+ function testDone() {
+ bc.postMessage({result: pass});
+ bc.close();
+ window.close();
+ }
+
+ function test() {
+ window.onpageshow = checkNavigationTypeEquals2;
+ window.location.href = 'goback.html';
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(test, 0);">
+ Testing bug 145971.
+ </body>
+</html>
diff --git a/docshell/test/navigation/test_bug1536471.html b/docshell/test/navigation/test_bug1536471.html
new file mode 100644
index 0000000000..f37aedba21
--- /dev/null
+++ b/docshell/test/navigation/test_bug1536471.html
@@ -0,0 +1,75 @@
+
+<!DOCTYPE HTML>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1536471
+ -->
+<head>
+ <title>Test for Bug 1536471</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="application/javascript">
+
+ let testWin;
+ async function test() {
+ // Open a new tab and load a document with an iframe inside
+ testWin = window.open("file_bug1536471.html");
+ await waitForLoad();
+ var iframe = testWin.document.getElementById("staticFrame");
+ is(testWin.history.length, 1, "Checking the number of session history entries when there is only one iframe");
+
+ // Navigate the iframe to different pages
+ await loadUriInFrame(iframe, "frame1.html");
+ is(testWin.history.length, 2, "Checking the number of session history entries after having navigated a single iframe 1 time");
+ await loadUriInFrame(iframe, "frame2.html");
+ is(testWin.history.length, 3, "Checking the number of session history entries after having navigated a single iframe 2 times");
+ await loadUriInFrame(iframe, "frame3.html");
+ is(testWin.history.length, 4, "Checking the number of session history entries after having navigated a single iframe 3 times");
+
+ // Reload the top document
+ testWin.location.reload(true);
+ await waitForLoad();
+ is(testWin.history.length, 1, "Checking the number of session history entries after reloading the top document");
+
+ testWin.close();
+ SimpleTest.finish();
+ }
+
+ async function waitForLoad() {
+ await new Promise(resolve => {
+ window.bodyOnLoad = function() {
+ setTimeout(resolve, 0);
+ window.bodyOnLoad = undefined;
+ };
+ });
+ }
+
+ async function iframeOnload(frame) {
+ return new Promise(resolve => {
+ frame.addEventListener("load", () => {
+ setTimeout(resolve, 0);
+ }, {once: true});
+ });
+ }
+
+ async function loadUriInFrame(frame, uri) {
+ let onloadPromise = iframeOnload(frame);
+ frame.src = uri;
+ await onloadPromise;
+ }
+ </script>
+</head>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1536471">Mozilla Bug </a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+<body onload="test()">
+</body>
+</html>
+
diff --git a/docshell/test/navigation/test_bug1583110.html b/docshell/test/navigation/test_bug1583110.html
new file mode 100644
index 0000000000..f1c1b65e4d
--- /dev/null
+++ b/docshell/test/navigation/test_bug1583110.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>test bug 1583110</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ var bc = new BroadcastChannel("bug1583110");
+ var pageshowCount = 0;
+ bc.onmessage = function(event) {
+ ok(event.data.type == "pageshow");
+ ++pageshowCount;
+ if (pageshowCount == 1) {
+ bc.postMessage("loadnext");
+ } else if (pageshowCount == 2) {
+ bc.postMessage("back");
+ } else {
+ ok(event.data.persisted, "Should have persisted the first page");
+ bc.close();
+ SimpleTest.finish();
+ }
+ }
+
+ function test() {
+ window.open("file_bug1583110.html", "", "noopener");
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1609475.html b/docshell/test/navigation/test_bug1609475.html
new file mode 100644
index 0000000000..4dbe7d17d6
--- /dev/null
+++ b/docshell/test/navigation/test_bug1609475.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 1609475</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1609475">Mozilla Bug 1609475</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_bug1609475.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1699721.html b/docshell/test/navigation/test_bug1699721.html
new file mode 100644
index 0000000000..687c5306cf
--- /dev/null
+++ b/docshell/test/navigation/test_bug1699721.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script type="text/javascript">
+ add_task(async function() {
+ let popup = window.open("blank.html");
+
+ info("opened popup");
+ await new Promise(resolve => {
+ popup.addEventListener("load", resolve, { once: true });
+ });
+
+ info("popup blank.html loaded");
+ let tell_opener = new URL("file_tell_opener.html", location.href);
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ let xorigin_url = new URL(tell_opener.pathname, "http://example.com");
+
+ let resolveStartedUnload;
+ let startedUnload = new Promise(resolve => {
+ resolveStartedUnload = resolve;
+ });
+ let didFinishUnload = false;
+
+ let finishUnload = false;
+ popup.addEventListener("unload", function() {
+ resolveStartedUnload();
+ try {
+ // Spin a nested event loop in unload until we set `finishUnload`.
+ SpecialPowers.Services.tm.spinEventLoopUntil(
+ "Test(test_switch_back_nested.html)", () => finishUnload);
+ } finally {
+ info("exiting from unload nested event loop...");
+ didFinishUnload = true;
+ }
+ });
+
+ info("wait for message from popup");
+ let messagePromise = new Promise(resolve => {
+ addEventListener("message", evt => {
+ resolve();
+ }, { once: true });
+ });
+ popup.location = xorigin_url.href;
+ await messagePromise;
+
+ info("popup loaded, ensuring we're in unload");
+ await startedUnload;
+ is(didFinishUnload, false, "unload shouldn't have finished");
+
+ let switchStarted = SpecialPowers.spawnChrome([], async () => {
+ await new Promise(resolve => {
+ async function observer(subject, topic) {
+ is(topic, "http-on-examine-response");
+
+ let uri = subject.QueryInterface(Ci.nsIChannel).URI;
+ if (!uri.filePath.endsWith("file_tell_opener.html")) {
+ return;
+ }
+
+ Services.obs.removeObserver(observer, "http-on-examine-response");
+
+ // spin the event loop a few times to ensure we resolve after the process switch
+ for (let i = 0; i < 10; ++i) {
+ await new Promise(res => Services.tm.dispatchToMainThread(res));
+ }
+
+ info("resolving!");
+ resolve();
+ }
+ Services.obs.addObserver(observer, "http-on-examine-response");
+ });
+ });
+
+ info("Navigating back to the current process");
+ await SpecialPowers.spawn(popup, [tell_opener.href], (href) => {
+ content.location.href = href;
+ });
+
+ let messagePromise2 = new Promise(resolve => {
+ addEventListener("message", evt => {
+ resolve();
+ }, { once: true });
+ });
+
+ info("Waiting for the process switch to start");
+ await switchStarted;
+
+ // Finish unloading, and wait for the unload to complete
+ is(didFinishUnload, false, "unload shouldn't be finished");
+ finishUnload = true;
+ await new Promise(resolve => setTimeout(resolve, 0));
+ is(didFinishUnload, true, "unload should be finished");
+
+ info("waiting for navigation to complete");
+ await messagePromise2;
+
+ info("closing popup");
+ popup.close();
+
+ ok(true, "Didn't crash");
+ });
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1706090.html b/docshell/test/navigation/test_bug1706090.html
new file mode 100644
index 0000000000..293148b9c6
--- /dev/null
+++ b/docshell/test/navigation/test_bug1706090.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1706090</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ var bc = new BroadcastChannel("bug1706090");
+ var pageshowCount = 0;
+ bc.onmessage = function(event) {
+ if (event.data.type == "pageshow") {
+ ++pageshowCount;
+ if (pageshowCount == 1) {
+ is(event.data.persisted, false, "Shouldn't have persisted the initial load.");
+ bc.postMessage("sameOrigin");
+ } else if (pageshowCount == 2) {
+ bc.postMessage("back");
+ } else if (pageshowCount == 3) {
+ is(event.data.persisted, false, "Shouldn't have persisted same origin load.");
+ bc.postMessage("crossOrigin");
+ } else if (pageshowCount == 4) {
+ is(event.data.persisted, true, "Should have persisted cross origin load.");
+ bc.postMessage("sameSite");
+ } else if (pageshowCount == 5) {
+ is(event.data.persisted, false, "Shouldn't have persisted same site load.");
+ bc.postMessage("close");
+ }
+ } else if (event.data == "closed") {
+ bc.close();
+ SimpleTest.finish();
+ }
+ };
+
+ function runTest() {
+ SpecialPowers.pushPrefEnv({set: [["docshell.shistory.bfcache.allow_unload_listeners", true]]}, () => {
+ window.open("file_bug1706090.html", "", "noopener");
+ });
+ }
+ </script>
+</head>
+<body onload="runTest()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1745638.html b/docshell/test/navigation/test_bug1745638.html
new file mode 100644
index 0000000000..594c464da3
--- /dev/null
+++ b/docshell/test/navigation/test_bug1745638.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>bug 1745638</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ // This test triggers an assertion in the old session history
+ // implementation.
+ SimpleTest.expectAssertions(0, 1);
+
+ SimpleTest.waitForExplicitFinish();
+ var testWindow;
+ var loadCount = 0;
+ function test() {
+ testWindow = window.open("file_bug1745638.html");
+ }
+
+ function pageLoaded() {
+ ++loadCount;
+ is(testWindow.document.getElementById('testFrame').contentDocument.body.innerHTML,
+ "passed",
+ "Iframe's textual content should be 'passed'.");
+ if (loadCount == 1) {
+ testWindow.history.go(0);
+ } else {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+ }
+
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1747019.html b/docshell/test/navigation/test_bug1747019.html
new file mode 100644
index 0000000000..c7995737df
--- /dev/null
+++ b/docshell/test/navigation/test_bug1747019.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test session history and caching</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ var win;
+ var loadCount = 0;
+ var initialContent;
+ // The test loads first a page in a new window, then using
+ // form submission loads another page and then using form submission
+ // again loads third page. That page triggers history.go(-1).
+ // The second page is loaded now again and should have the same content
+ // as it had before.
+ function test() {
+ win = window.open("cache_control_max_age_3600.sjs?initial");
+ window.onmessage = (e) => {
+ is(e.data, "loaded", "Should get load message 'loaded'");
+ ++loadCount;
+ if (loadCount == 1) {
+ win.document.forms[0].submit();
+ } else if (loadCount == 2) {
+ initialContent = win.document.body.textContent;
+ info("The initial content is [" + initialContent + "].");
+ win.document.forms[0].submit();
+ } else if (loadCount == 3) {
+ let newContent = win.document.body.textContent;
+ info("The new content is [" + newContent + "].");
+ win.close();
+ is(initialContent, newContent, "Should have loaded the page from cache.");
+ SimpleTest.finish();
+ } else {
+ ok(false, "Unexpected load count.");
+ }
+ }
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1750973.html b/docshell/test/navigation/test_bug1750973.html
new file mode 100644
index 0000000000..9f87075b90
--- /dev/null
+++ b/docshell/test/navigation/test_bug1750973.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>The layout state restoration when reframing the root element</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ function test() {
+ window.open("file_bug1750973.html");
+ }
+ </script>
+</head>
+<body onload="setTimeout(test)">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1758664.html b/docshell/test/navigation/test_bug1758664.html
new file mode 100644
index 0000000000..662242e44a
--- /dev/null
+++ b/docshell/test/navigation/test_bug1758664.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1758664</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ function test() {
+ window.open("file_bug1758664.html");
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug270414.html b/docshell/test/navigation/test_bug270414.html
new file mode 100644
index 0000000000..0635e32888
--- /dev/null
+++ b/docshell/test/navigation/test_bug270414.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+/* eslint-disable no-useless-concat */
+/* global window0:true, window1:true, window2:true, window3:true */
+var headerHTML = "<html><head>" +
+ "<script src='/tests/SimpleTest/EventUtils.js'><\/script>" +
+ "<script src='NavigationUtils.js'><\/script>" +
+ "</head><body>";
+var footerHTML = "</body></html>";
+
+function testChild0() {
+ if (!window.window0) {
+ window0 = window.open("", "window0", "width=10,height=10");
+ window0.document.open();
+ // eslint-disable-next-line no-unsanitized/method
+ window0.document.write(headerHTML);
+ window0.document.write("<script>navigateByLocation(opener.frames[0])<\/script>");
+ // eslint-disable-next-line no-unsanitized/method
+ window0.document.write(footerHTML);
+ window0.document.close();
+ }
+}
+
+function testChild1() {
+ if (!window.window1) {
+ window1 = window.open("", "window1", "width=10,height=10");
+ window1.document.open();
+ // eslint-disable-next-line no-unsanitized/method
+ window1.document.write(headerHTML);
+ window1.document.write("<script>navigateByOpen('child1');<\/script>");
+ // eslint-disable-next-line no-unsanitized/method
+ window1.document.write(footerHTML);
+ window1.document.close();
+ }
+}
+
+function testChild2() {
+ if (!window.window2) {
+ window2 = window.open("", "window2", "width=10,height=10");
+ window2.document.open();
+ // eslint-disable-next-line no-unsanitized/method
+ window2.document.write(headerHTML);
+ window2.document.write("<script>navigateByForm('child2');<\/script>");
+ // eslint-disable-next-line no-unsanitized/method
+ window2.document.write(footerHTML);
+ window2.document.close();
+ }
+}
+
+function testChild3() {
+ if (!window.window3) {
+ window3 = window.open("", "window3", "width=10,height=10");
+ window3.document.open();
+ // eslint-disable-next-line no-unsanitized/method
+ window3.document.write(headerHTML);
+ window3.document.write("<script>navigateByHyperlink('child3');<\/script>");
+ // eslint-disable-next-line no-unsanitized/method
+ window3.document.write(footerHTML);
+ window3.document.close();
+ }
+}
+
+add_task(async function() {
+ await waitForFinishedFrames(4);
+
+ await isNavigated(frames[0], "Should be able to navigate on-domain opener's children by setting location.");
+ await isNavigated(frames[1], "Should be able to navigate on-domain opener's children by calling window.open.");
+ await isNavigated(frames[2], "Should be able to navigate on-domain opener's children by submitting form.");
+ await isNavigated(frames[3], "Should be able to navigate on-domain opener's children by targeted hyperlink.");
+
+ window0.close();
+ window1.close();
+ window2.close();
+ window3.close();
+
+ await cleanupWindows();
+});
+
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=270414">Mozilla Bug 270414</a>
+<div id="frames">
+<iframe onload="testChild0();" name="child0" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe onload="testChild1();" name="child1" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe onload="testChild2();" name="child2" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe onload="testChild3();" name="child3" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+</div>
+<pre id="test">
+<script type="text/javascript">
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug278916.html b/docshell/test/navigation/test_bug278916.html
new file mode 100644
index 0000000000..9e2335721e
--- /dev/null
+++ b/docshell/test/navigation/test_bug278916.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+<script>
+window.onload = async function() {
+ document.getElementById("link0").href = target_url;
+ sendMouseEvent({type: "click"}, "link0");
+
+ await waitForFinishedFrames(1);
+
+ var array_of_frames = await getFramesByName("window0");
+ is(array_of_frames.length, 1, "Should only open one window using a fancy hyperlink.");
+
+ for (var i = 0; i < array_of_frames.length; ++i)
+ array_of_frames[i].close();
+
+ await cleanupWindows();
+ SimpleTest.finish();
+};
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=278916">Mozilla Bug 278916</a>
+<div id="links">
+<a id="link0" target="window0" onclick="window.open('', 'window0', 'width=10,height=10');">This is a fancy hyperlink</a>
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug279495.html b/docshell/test/navigation/test_bug279495.html
new file mode 100644
index 0000000000..245ed14ed4
--- /dev/null
+++ b/docshell/test/navigation/test_bug279495.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+<script>
+window.onload = async function() {
+ document.getElementById("link0").href = target_url;
+ document.getElementById("link1").href = target_url;
+
+ sendMouseEvent({type: "click"}, "link0");
+ sendMouseEvent({type: "click"}, "link1");
+
+ await waitForFinishedFrames(2);
+ await countAndClose("window0", 1);
+ await countAndClose("window1", 1);
+
+ await cleanupWindows();
+ SimpleTest.finish();
+};
+
+async function countAndClose(name, expected_count) {
+ var array_of_frames = await getFramesByName(name);
+ is(array_of_frames.length, expected_count,
+ "Should only open " + expected_count +
+ " window(s) with name " + name + " using a fancy hyperlink.");
+}
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=279495">Mozilla Bug 279495</a>
+<div id="links">
+<a id="link0" target="window0" onclick="window.open('blank.html', 'window0', 'width=10,height=10');">This is a fancy hyperlink</a>
+<a id="link1" target="window1" onclick="window.open('https://test1.example.org/tests/docshell/test/navigation/blank.html', 'window1', 'width=10,height=10');">This is a fancy hyperlink</a>
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug344861.html b/docshell/test/navigation/test_bug344861.html
new file mode 100644
index 0000000000..5ab8809d27
--- /dev/null
+++ b/docshell/test/navigation/test_bug344861.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=344861
+-->
+<head>
+ <title>Test for Bug 344861</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=344861">Mozilla Bug 344861</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 344861 */
+SimpleTest.waitForExplicitFinish();
+
+var newwindow = window.open("/", "testwindow", "width=200,height=200");
+newwindow.onload = function() {
+ is(newwindow.innerHeight, 200, "window.open has correct height dimensions");
+ is(newwindow.innerWidth, 200, "window.open has correct width dimensions");
+ SimpleTest.finish();
+ newwindow.close();
+};
+</script>
+</pre>
+</body>
+</html>
+
+
diff --git a/docshell/test/navigation/test_bug386782.html b/docshell/test/navigation/test_bug386782.html
new file mode 100644
index 0000000000..f6ad85f5a6
--- /dev/null
+++ b/docshell/test/navigation/test_bug386782.html
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=386782
+-->
+<head>
+ <title>Test for Bug 386782</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script>
+
+ // This tests if we can load a document whose root is in designMode,
+ // edit it, navigate to a new page, navigate back, still edit, and still
+ // undo/redo. Note that this is different from the case where the
+ // designMode document is in a frame inside the window, as this means
+ // the editable region is not in the root docshell (a less complicated case).
+
+ var gTests = [
+ {
+ // <html><body><p>designModeDocument</p></body></html>
+ url: "file_bug386782_designmode.html",
+ name: "designModeNavigate",
+ onload(doc) { doc.designMode = "on"; },
+ expectedBodyBeforeEdit: "<p>designModeDocument</p>",
+ expectedBodyAfterEdit: "<p>EDITED designModeDocument</p>",
+ expectedBodyAfterSecondEdit: "<p>EDITED TWICE designModeDocument</p>",
+ },
+ {
+ // <html><body contentEditable="true"><p>contentEditable</p></body></html>
+ url: "file_bug386782_contenteditable.html",
+ name: "contentEditableNavigate",
+ expectedBodyBeforeEdit: "<p>contentEditable</p>",
+ expectedBodyAfterEdit: "EDITED <br><p>contentEditable</p>",
+ expectedBodyAfterSecondEdit: "EDITED TWICE <br><p>contentEditable</p>",
+ },
+ ];
+
+ var gTest = null;
+
+ add_task(async () => {
+ while (gTests.length) {
+ gTest = gTests.shift();
+ await runTest();
+ }
+ });
+
+ async function runTest() {
+ gTest.window = window.open(gTest.url, gTest.name, "width=500,height=500");
+ let e = await new Promise(r => window.onmessage = r);
+ is(e.data.persisted, false, "Initial load cannot be persisted");
+ if ("onload" in gTest) {
+ gTest.onload(gTest.window.document);
+ }
+ await SimpleTest.promiseFocus(gTest.window);
+
+ gTest.window.document.body.focus();
+
+ // WARNING: If the following test fails, give the setTimeout() in the onload()
+ // a bit longer; the doc hasn't had enough time to setup its editor.
+ is(gTest.window.document.body.innerHTML, gTest.expectedBodyBeforeEdit, "Is doc setup yet");
+ sendString("EDITED ", gTest.window);
+ is(gTest.window.document.body.innerHTML, gTest.expectedBodyAfterEdit, "Editing failed.");
+
+ gTest.window.location = "about:blank";
+ await new Promise(r => gTest.window.onpagehide = r);
+ // The active document is updated synchronously after "pagehide" (and
+ // its associated microtasks), so, after waiting for the next global
+ // task, gTest.window will be proxying the realm associated with the
+ // "about:blank" document.
+ // https://html.spec.whatwg.org/multipage/browsing-the-web.html#update-the-session-history-with-the-new-page
+ await new Promise(r => setTimeout(r));
+ is(gTest.window.location.href, "about:blank", "location.href");
+ await SimpleTest.promiseFocus(gTest.window, true);
+
+ gTest.window.history.back();
+ e = await new Promise(r => window.onmessage = r);
+ // Skip the test if the page is not loaded from the bf-cache when going back.
+ if (e.data.persisted) {
+ checkStillEditable();
+ }
+ gTest.window.close();
+ }
+
+ function checkStillEditable() {
+ // Check that the contents are correct.
+ is(gTest.window.document.body.innerHTML, gTest.expectedBodyAfterEdit, "Edited contents still correct?");
+
+ // Check that we can undo/redo and the contents are correct.
+ gTest.window.document.execCommand("undo", false, null);
+ is(gTest.window.document.body.innerHTML, gTest.expectedBodyBeforeEdit, "Can we undo?");
+
+ gTest.window.document.execCommand("redo", false, null);
+ is(gTest.window.document.body.innerHTML, gTest.expectedBodyAfterEdit, "Can we redo?");
+
+ // Check that we can still edit the page.
+ gTest.window.document.body.focus();
+ sendString("TWICE ", gTest.window);
+ is(gTest.window.document.body.innerHTML, gTest.expectedBodyAfterSecondEdit, "Can we still edit?");
+ }
+
+ </script>
+
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=386782">Mozilla Bug 386782</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 386782 */
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug430624.html b/docshell/test/navigation/test_bug430624.html
new file mode 100644
index 0000000000..49afb023b9
--- /dev/null
+++ b/docshell/test/navigation/test_bug430624.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=430624
+-->
+<head>
+ <title>Test for Bug 430624</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=430624">Mozilla Bug 430624</a>
+<p id="display"></p>
+
+
+
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 430624 */
+
+function onLoad() {
+ window.frames[0].frameElement.onload = onReload;
+ // eslint-disable-next-line no-self-assign
+ window.frames[0].frameElement.srcdoc = window.frames[0].frameElement.srcdoc;
+}
+
+function onReload() {
+ var iframe = window.frames[0].frameElement;
+ SimpleTest.waitForFocus(doTest, iframe.contentWindow);
+ iframe.contentDocument.body.focus();
+}
+
+function doTest() {
+ var bodyElement = window.frames[0].frameElement.contentDocument.body;
+ bodyElement.focus();
+ sendString("Still ", window.frames[0].frameElement.contentWindow);
+
+ is(bodyElement.innerHTML, "Still contentEditable", "Check we're contentEditable after reload");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+
+<iframe onload="onLoad()" srcdoc="<body contenteditable>contentEditable</body>"></iframe>
+
+</body>
+</html>
+
diff --git a/docshell/test/navigation/test_bug430723.html b/docshell/test/navigation/test_bug430723.html
new file mode 100644
index 0000000000..dd85c7fb08
--- /dev/null
+++ b/docshell/test/navigation/test_bug430723.html
@@ -0,0 +1,124 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=430723
+-->
+<head>
+ <title>Test for Bug 430723</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=430723">Mozilla Bug 430723</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+// <![CDATA[
+
+/** Test for Bug 430723 */
+
+var BASE_URI = "http://mochi.test:8888/tests/docshell/test/navigation/";
+var gTallRedBoxURI = BASE_URI + "redbox_bug430723.html";
+var gTallBlueBoxURI = BASE_URI + "bluebox_bug430723.html";
+
+window.onload = runTest;
+
+var testWindow;
+var testNum = 0;
+
+var smoothScrollPref = "general.smoothScroll";
+function runTest() {
+ SpecialPowers.pushPrefEnv({"set": [[smoothScrollPref, false]]}, function() {
+ testWindow = window.open(gTallRedBoxURI, "testWindow", "width=300,height=300,location=yes,scrollbars=yes");
+ });
+}
+
+var nextTest = function() {
+ testNum++;
+ switch (testNum) {
+ case 1: setTimeout(step1, 0); break;
+ case 2: setTimeout(step2, 0); break;
+ case 3: setTimeout(step3, 0); break;
+ }
+};
+
+var step1 = function() {
+ window.is(String(testWindow.location), gTallRedBoxURI, "Ensure red page loaded.");
+
+ // Navigate down and up.
+ is(testWindow.document.body.scrollTop, 0,
+ "Page1: Ensure the scrollpane is at the top before we start scrolling.");
+ testWindow.addEventListener("scroll", function() {
+ isnot(testWindow.document.body.scrollTop, 0,
+ "Page1: Ensure we can scroll down.");
+ SimpleTest.executeSoon(step1_2);
+ }, {capture: true, once: true});
+ sendKey("DOWN", testWindow);
+
+ function step1_2() {
+ testWindow.addEventListener("scroll", function() {
+ is(testWindow.document.body.scrollTop, 0,
+ "Page1: Ensure we can scroll up, back to the top.");
+
+ // Nav to blue box page. This should fire step2.
+ testWindow.location = gTallBlueBoxURI;
+ }, {capture: true, once: true});
+ sendKey("UP", testWindow);
+ }
+};
+
+
+var step2 = function() {
+ window.is(String(testWindow.location), gTallBlueBoxURI, "Ensure blue page loaded.");
+
+ // Scroll around a bit.
+ is(testWindow.document.body.scrollTop, 0,
+ "Page2: Ensure the scrollpane is at the top before we start scrolling.");
+
+ var scrollTest = function() {
+ if (++count < 2) {
+ SimpleTest.executeSoon(function() { sendKey("DOWN", testWindow); });
+ } else {
+ testWindow.removeEventListener("scroll", scrollTest, true);
+
+ isnot(testWindow.document.body.scrollTop, 0,
+ "Page2: Ensure we could scroll.");
+
+ // Navigate backwards. This should fire step3.
+ testWindow.history.back();
+ }
+ };
+
+ var count = 0;
+ testWindow.addEventListener("scroll", scrollTest, true);
+ sendKey("DOWN", testWindow);
+};
+
+var step3 = function() {
+ window.is(String(testWindow.location), gTallRedBoxURI,
+ "Ensure red page restored from history.");
+
+ // Check we can still scroll with the keys.
+ is(testWindow.document.body.scrollTop, 0,
+ "Page1Again: Ensure scroll pane at top before we scroll.");
+ testWindow.addEventListener("scroll", function() {
+ isnot(testWindow.document.body.scrollTop, 0,
+ "Page2Again: Ensure we can still scroll.");
+
+ testWindow.close();
+ window.SimpleTest.finish();
+ }, {capture: true, once: true});
+ sendKey("DOWN", testWindow);
+};
+
+SimpleTest.waitForExplicitFinish();
+
+// ]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_child.html b/docshell/test/navigation/test_child.html
new file mode 100644
index 0000000000..87237471cd
--- /dev/null
+++ b/docshell/test/navigation/test_child.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+if (!navigator.platform.startsWith("Win")) {
+ SimpleTest.expectAssertions(0, 1);
+}
+
+window.onload = async function() {
+ navigateByLocation(frames[0]);
+ navigateByOpen("child1");
+ navigateByForm("child2");
+ navigateByHyperlink("child3");
+
+ await waitForFinishedFrames(4);
+ await isNavigated(frames[0], "Should be able to navigate off-domain child by setting location.");
+ await isNavigated(frames[1], "Should be able to navigate off-domain child by calling window.open.");
+ await isNavigated(frames[2], "Should be able to navigate off-domain child by submitting form.");
+ await isNavigated(frames[3], "Should be able to navigate off-domain child by targeted hyperlink.");
+
+ await cleanupWindows();
+ SimpleTest.finish();
+};
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
+<div id="frames">
+<iframe name="child0" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe name="child1" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe name="child2" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe name="child3" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_contentpolicy_block_window.html b/docshell/test/navigation/test_contentpolicy_block_window.html
new file mode 100644
index 0000000000..7ce337c131
--- /dev/null
+++ b/docshell/test/navigation/test_contentpolicy_block_window.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1329288
+-->
+<head>
+ <title>Test for Bug 1329288</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1329288">Mozilla Bug 1329288</a>
+
+
+<!-- have a testlink which we can use for the test to open a new window -->
+<a href="http://test1.example.org/tests/docshell/test/navigation/file_contentpolicy_block_window.html"
+ target="_blank"
+ id="testlink">This is a link</a>
+
+<script class="testbody" type="text/javascript">
+/*
+ * Description of the test:
+ * The test tries to open a new window and makes sure that a registered contentPolicy
+ * gets called with the right (a non null) 'context' for the TYPE_DOCUMENT load.
+ */
+
+const Ci = SpecialPowers.Ci;
+
+var categoryManager = SpecialPowers.Services.catMan;
+var componentManager = SpecialPowers.Components.manager
+ .QueryInterface(Ci.nsIComponentRegistrar);
+
+// Content policy / factory implementation for the test
+var policyID = SpecialPowers.wrap(SpecialPowers.Components).ID("{b80e19d0-878f-d41b-2654-194714a4115c}");
+var policyName = "@mozilla.org/testpolicy;1";
+var policy = {
+ // nsISupports implementation
+ // eslint-disable-next-line mozilla/use-chromeutils-generateqi
+ QueryInterface(iid) {
+ iid = SpecialPowers.wrap(iid);
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIFactory) ||
+ iid.equals(Ci.nsIContentPolicy))
+ return this;
+ throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ // nsIFactory implementation
+ createInstance(iid) {
+ return this.QueryInterface(iid);
+ },
+
+ // nsIContentPolicy implementation
+ shouldLoad(contentLocation, loadInfo, mimeTypeGuess) {
+ let contentType = loadInfo.externalContentPolicyType;
+ let context = loadInfo.loadingContext;
+
+ if (SpecialPowers.wrap(contentLocation).spec !== document.getElementById("testlink").href) {
+ // not the URI we are looking for, allow the load
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+
+ is(contentType, Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ "needs to be type document load");
+ ok(context, "context is not allowed to be null");
+ ok(context.name.endsWith("test_contentpolicy_block_window.html"),
+ "context should be the current window");
+
+ // remove the policy and finish test.
+ categoryManager.deleteCategoryEntry("content-policy", policyName, false);
+
+ setTimeout(function() {
+ // Component must be unregistered delayed, otherwise other content
+ // policy will not be removed from the category correctly
+ componentManager.unregisterFactory(policyID, policy);
+ }, 0);
+
+ SimpleTest.finish();
+ return Ci.nsIContentPolicy.REJECT_REQUEST;
+ },
+
+ shouldProcess(contentLocation, loadInfo, mimeTypeGuess) {
+ return Ci.nsIContentPolicy.ACCEPT;
+ },
+};
+
+policy = SpecialPowers.wrapCallbackObject(policy);
+componentManager.registerFactory(policyID, "Test content policy", policyName, policy);
+categoryManager.addCategoryEntry("content-policy", policyName, policyName, false, true);
+
+SimpleTest.waitForExplicitFinish();
+
+// now everything is set up, let's start the test
+document.getElementById("testlink").click();
+
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_docshell_gotoindex.html b/docshell/test/navigation/test_docshell_gotoindex.html
new file mode 100644
index 0000000000..992c9c9dbe
--- /dev/null
+++ b/docshell/test/navigation/test_docshell_gotoindex.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1684310</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ function test() {
+ /*
+ * This test is for nsIWebNavigation.gotoIndex.
+ *
+ * The test
+ * - opens a new window
+ * - loads a page there
+ * - loads another page
+ * - navigates to some fragments in the page
+ * - goes back to one of the fragments
+ * - tries to go back to the initial page.
+ */
+ window.open("file_docshell_gotoindex.html");
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_dynamic_frame_forward_back.html b/docshell/test/navigation/test_dynamic_frame_forward_back.html
new file mode 100644
index 0000000000..f3a349e09a
--- /dev/null
+++ b/docshell/test/navigation/test_dynamic_frame_forward_back.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 508537</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=508537">Mozilla Bug 508537</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_bug508537_1.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_evict_from_bfcache.html b/docshell/test/navigation/test_evict_from_bfcache.html
new file mode 100644
index 0000000000..0b1eb2fca4
--- /dev/null
+++ b/docshell/test/navigation/test_evict_from_bfcache.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Evict a page from bfcache</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ /*
+ * This test checks that a page can be evicted from bfcache. Sending a
+ * message to an open BroadcastChannel is used for this.
+ *
+ * First the test opens a window and loads a page there. Another page is then
+ * loaded and then session history is navigated back to check if bfcache is
+ * enabled. If not, close message is sent to close the opened window and this
+ * controller page will finish the test.
+ * If bfcache is enabled, session history goes forward, but the
+ * BroadcastChannel in the page isn't closed. Then sending the message to go
+ * back again should evict the bfcached page.
+ * Close message is sent and window closed and test finishes.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+ var bc = new BroadcastChannel("evict_from_bfcache");
+ var pageshowCount = 0;
+ bc.onmessage = function(event) {
+ if (event.data.type == "pageshow") {
+ ++pageshowCount;
+ info("pageshow " + pageshowCount);
+ if (pageshowCount == 1) {
+ bc.postMessage("nextpage");
+ } else if (pageshowCount == 2) {
+ bc.postMessage("back");
+ } else if (pageshowCount == 3) {
+ if (!event.data.persisted) {
+ ok(true, "BFCache isn't enabled.");
+ bc.postMessage("close");
+ } else {
+ bc.postMessage("forward");
+ }
+ } else if (pageshowCount == 4) {
+ bc.postMessage("back");
+ } else if (pageshowCount == 5) {
+ ok(!event.data.persisted,
+ "The page should have been evicted from BFCache");
+ bc.postMessage("close");
+ }
+ } else if (event.data == "closed") {
+ SimpleTest.finish();
+ }
+ }
+
+ function runTest() {
+ window.open("file_evict_from_bfcache.html", "", "noopener");
+ }
+ </script>
+</head>
+<body onload="runTest()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_fragment_handling_during_load.html b/docshell/test/navigation/test_fragment_handling_during_load.html
new file mode 100644
index 0000000000..9c082c2ecf
--- /dev/null
+++ b/docshell/test/navigation/test_fragment_handling_during_load.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for fragment navigation during load</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=978408">Mozilla Bug 978408</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_fragment_handling_during_load.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_grandchild.html b/docshell/test/navigation/test_grandchild.html
new file mode 100644
index 0000000000..10cf610664
--- /dev/null
+++ b/docshell/test/navigation/test_grandchild.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 200px; }
+ </style>
+<script>
+if (!navigator.platform.startsWith("Win")) {
+ SimpleTest.expectAssertions(0, 1);
+}
+
+window.onload = async function() {
+ navigateByLocation(frames[0].frames[0]);
+ navigateByOpen("child1_child0");
+ navigateByForm("child2_child0");
+ navigateByHyperlink("child3_child0");
+
+ await waitForFinishedFrames(4);
+ await isNavigated(frames[0].frames[0], "Should be able to navigate off-domain grandchild by setting location.");
+ await isNavigated(frames[1].frames[0], "Should be able to navigate off-domain grandchild by calling window.open.");
+ await isNavigated(frames[2].frames[0], "Should be able to navigate off-domain grandchild by submitting form.");
+ await isNavigated(frames[3].frames[0], "Should be able to navigate off-domain grandchild by targeted hyperlink.");
+
+ await cleanupWindows();
+ SimpleTest.finish();
+};
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
+<div id="frames">
+<iframe name="child0" src="http://test1.example.org:80/tests/docshell/test/navigation/parent.html"></iframe>
+<iframe name="child1" src="http://test1.example.org:80/tests/docshell/test/navigation/parent.html"></iframe>
+<iframe name="child2" src="http://test1.example.org:80/tests/docshell/test/navigation/parent.html"></iframe>
+<iframe name="child3" src="http://test1.example.org:80/tests/docshell/test/navigation/parent.html"></iframe>
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_load_history_entry.html b/docshell/test/navigation/test_load_history_entry.html
new file mode 100644
index 0000000000..8ca3fcb913
--- /dev/null
+++ b/docshell/test/navigation/test_load_history_entry.html
@@ -0,0 +1,196 @@
+
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="application/javascript" src="/tests/SimpleTest/SpecialPowers.js"></script>
+ <script type="application/javascript">
+ /*
+ * Perform the following steps.
+ * 1) Go to file_load_history_entry_page_with_two_links.html, which contains two links, 'link1' and 'link2'
+ * 2) Click on 'link1' to be taken to file_load_history_entry_page_with_two_links.html#1
+ * 3) Click on 'link2' to be taken to file_load_history_entry_page_with_two_links.html#2
+ * 4) Go to file_load_history_entry_page_with_one_link.html
+ * 5) Push state to go to file_load_history_entry_page_with_one_link.html#1
+ *
+ * After each step
+ * - Check the number of session history entries
+ * - Reload the document and do the above again
+ * - Navigate back and check the correct history index
+ * - Navigate forward and check the correct history index and location
+ */
+ async function test() {
+ let testWin;
+ var promise;
+ var previousLocation;
+ var numSHEntries = 0;
+
+ // Step 1. Open a new tab and load a document with two links inside
+ // Now we are at file_load_history_entry_page_with_two_links.html
+ numSHEntries++;
+ promise = waitForLoad();
+ testWin = window.open("file_load_history_entry_page_with_two_links.html");
+ await promise;
+
+ let shistory = SpecialPowers.wrap(testWin)
+ .docShell
+ .QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
+ .sessionHistory;
+
+ // Step 2. Navigate the document by clicking on the 1st link
+ // Now we are at file_load_history_entry_page_with_two_links.html#1
+ numSHEntries++;
+ previousLocation = testWin.location.href;
+ await clickLink(testWin, "link1");
+ await doAfterEachTest(testWin, shistory, numSHEntries, previousLocation);
+
+ // Step 3. Navigate the document by clicking the 2nd link
+ // Now we are file_load_history_entry_page_with_two_links.html#2
+ numSHEntries++;
+ previousLocation = testWin.location.href;
+ await clickLink(testWin, "link2");
+ await doAfterEachTest(testWin, shistory, numSHEntries, previousLocation);
+
+ // Step 4. Navigate the document to a different page
+ // Now we are at file_load_history_entry_page_with_one_link.html
+ numSHEntries++;
+ previousLocation = testWin.location.href;
+ promise = waitForLoad();
+ testWin.location = "file_load_history_entry_page_with_one_link.html";
+ await promise;
+ await doAfterEachTest(testWin, shistory, numSHEntries, previousLocation,
+ true /* isCrossDocumentLoad */, false /* hashChangeExpected */);
+
+ // Step 5. Push some state
+ // Now we are at file_load_history_entry_page_with_one_link.html#1
+ numSHEntries++;
+ previousLocation = testWin.location.href;
+ testWin.history.pushState({foo: "bar"}, "", "#1");
+ is(testWin.history.length, numSHEntries, "Session history's length is correct after pushing state");
+ is(shistory.index, numSHEntries - 1 /* we haven't switched to new history entry yet*/,
+ "Session history's index is correct after pushing state");
+ await doAfterEachTest(testWin, shistory, numSHEntries, previousLocation);
+
+ // We are done with the test
+ testWin.close();
+ SimpleTest.finish();
+ }
+
+ /*
+ * @prevLocation
+ * if undefined, it is because there is no page to go back to
+ *
+ * @isCrossDocumentLoad
+ * did we just open a different document
+ * @hashChangeExpected
+ * Would we get a hash change event if we navigated backwards and forwards in history?
+ * This is framed with respect to the previous step, e.g. in the previous step was the
+ * hash different from the location we have navigated to just before calling this function?
+ * When we navigate forwards or backwards, we need to wait for this event
+ * because clickLink() also waits for hashchange event and
+ * if this function gets called before clickLink(), sometimes hashchange
+ * events from this function will leak to clickLink.
+ */
+ async function doAfterEachTest(testWin, shistory, expectedNumSHEntries, prevLocation,
+ isCrossDocumentLoad = false, hashChangeExpected = true) {
+ var initialLocation = testWin.location.href;
+ var initialSHIndex = shistory.index;
+ var promise;
+ is(testWin.history.length, expectedNumSHEntries, "Session history's length is correct");
+
+ // Reload the document
+ promise = waitForLoad();
+ testWin.location.reload(true);
+ await promise;
+ is(testWin.history.length, expectedNumSHEntries, "Session history's length is correct after reloading");
+
+ if (prevLocation == undefined) {
+ return;
+ }
+
+ var hashChangePromise;
+ if (hashChangeExpected) {
+ hashChangePromise = new Promise(resolve => {
+ testWin.addEventListener("hashchange", resolve, {once: true});
+ });
+ }
+ // Navigate backwards
+ if (isCrossDocumentLoad) {
+ // Current page must have been a cross document load, so we just need to wait for
+ // document load to complete after we navigate the history back
+ // because popstate event will not be fired in this case
+ promise = waitForLoad();
+ } else {
+ promise = waitForPopstate(testWin);
+ }
+ testWin.history.back();
+ await promise;
+ if (hashChangeExpected) {
+ await hashChangePromise;
+ }
+ is(testWin.location.href, prevLocation, "Window location is correct after navigating back in history");
+ is(shistory.index, initialSHIndex - 1, "Session history's index is correct after navigating back in history");
+
+ // Navigate forwards
+ if (isCrossDocumentLoad) {
+ promise = waitForLoad();
+ } else {
+ promise = waitForPopstate(testWin);
+ }
+ if (hashChangeExpected) {
+ hashChangePromise = new Promise(resolve => {
+ testWin.addEventListener("hashchange", resolve, {once: true});
+ });
+ }
+ testWin.history.forward();
+ await promise;
+ if (hashChangeExpected) {
+ await hashChangePromise;
+ }
+ is(testWin.location.href, initialLocation, "Window location is correct after navigating forward in history");
+ is(shistory.index, initialSHIndex, "Session history's index is correct after navigating forward in history");
+ }
+
+ async function waitForLoad() {
+ return new Promise(resolve => {
+ window.bodyOnLoad = function() {
+ setTimeout(resolve, 0);
+ window.bodyOnLoad = undefined;
+ };
+ });
+ }
+
+ async function waitForPopstate(win) {
+ return new Promise(resolve => {
+ win.addEventListener("popstate", (e) => {
+ setTimeout(resolve, 0);
+ }, {once: true});
+ });
+ }
+
+ async function clickLink(win, id) {
+ var link = win.document.getElementById(id);
+ let clickPromise = new Promise(resolve => {
+ win.addEventListener("hashchange", resolve, {once: true});
+ });
+ link.click();
+ await clickPromise;
+ }
+
+ </script>
+</head>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1539482">Bug 1539482</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+<body onload="test()">
+</body>
+</html>
+
diff --git a/docshell/test/navigation/test_meta_refresh.html b/docshell/test/navigation/test_meta_refresh.html
new file mode 100644
index 0000000000..bda9a9fe73
--- /dev/null
+++ b/docshell/test/navigation/test_meta_refresh.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test meta refresh</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ let hasLoadedInitialOnce = false;
+ let bc = new BroadcastChannel("test_meta_refresh");
+ bc.onmessage = function(event) {
+ info(event.data.load || event.data);
+ if (event.data.load == "initial") {
+ if (!hasLoadedInitialOnce) {
+ hasLoadedInitialOnce = true;
+ bc.postMessage("loadnext");
+ } else {
+ bc.postMessage("ensuremetarefresh");
+ }
+ } else if (event.data.load == "nextpage") {
+ bc.postMessage("back");
+ } else if (event.data.load == "refresh") {
+ bc.postMessage("close");
+ } else if (event.data == "closed") {
+ ok(true, "Meta refresh page was loaded.");
+ SimpleTest.finish();
+ }
+ }
+
+ function test() {
+ window.open("file_meta_refresh.html?initial", "", "noopener");
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_navigation_type.html b/docshell/test/navigation/test_navigation_type.html
new file mode 100644
index 0000000000..75ea88bcbd
--- /dev/null
+++ b/docshell/test/navigation/test_navigation_type.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>performance.navigation.type</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ let bc = new BroadcastChannel("navigation_type");
+ let pageshowCount = 0;
+ bc.onmessage = function(event) {
+ if (event.data == "closed") {
+ bc.close();
+ SimpleTest.finish();
+ return;
+ }
+
+ ++pageshowCount;
+ if (pageshowCount == 1) {
+ is(event.data.navigationType, PerformanceNavigation.TYPE_NAVIGATE,
+ "Should have navigation type TYPE_NAVIGATE.");
+ bc.postMessage("loadNewPage");
+ } else if (pageshowCount == 2) {
+ is(event.data.navigationType, PerformanceNavigation.TYPE_NAVIGATE,
+ "Should have navigation type TYPE_NAVIGATE.");
+ bc.postMessage("back");
+ } else if (pageshowCount == 3) {
+ is(event.data.navigationType, PerformanceNavigation.TYPE_BACK_FORWARD ,
+ "Should have navigation type TYPE_BACK_FORWARD .");
+ bc.postMessage("close");
+ } else {
+ ok(false, "Unexpected load");
+ }
+ }
+
+ function test() {
+ window.open("file_navigation_type.html", "", "noopener");
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_nested_frames.html b/docshell/test/navigation/test_nested_frames.html
new file mode 100644
index 0000000000..c3b49e0e23
--- /dev/null
+++ b/docshell/test/navigation/test_nested_frames.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 1090918</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1090918">Mozilla Bug 1090918</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_nested_frames.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close()
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_new_shentry_during_history_navigation.html b/docshell/test/navigation/test_new_shentry_during_history_navigation.html
new file mode 100644
index 0000000000..0c9adc5280
--- /dev/null
+++ b/docshell/test/navigation/test_new_shentry_during_history_navigation.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test adding new session history entries while navigating to another one</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ var win;
+
+ function waitForMessage(msg) {
+ return new Promise(
+ function(resolve) {
+ window.addEventListener("message",
+ function(event) {
+ is(event.data, msg, "Got the expected message " + msg);
+ resolve();
+ }, { once: true }
+ );
+ }
+ );
+ }
+
+ async function test() {
+
+ let loadPromise = waitForMessage("load");
+ win = window.open("file_new_shentry_during_history_navigation_1.html");
+ await loadPromise;
+
+ loadPromise = waitForMessage("load");
+ win.location.href = "file_new_shentry_during_history_navigation_2.html";
+ await loadPromise;
+
+ let beforeunloadPromise = waitForMessage("beforeunload");
+ win.history.back();
+ await beforeunloadPromise;
+ await waitForMessage("load");
+
+ win.history.back();
+ SimpleTest.requestFlakyTimeout("Test that history.back() does not work on the initial entry.");
+ setTimeout(function() {
+ win.onmessage = null;
+ win.close();
+ testBfcache();
+ }, 500);
+ window.onmessage = function(event) {
+ ok(false, "Should not get a message " + event.data);
+ }
+ }
+
+ async function testBfcache() {
+ let bc = new BroadcastChannel("new_shentry_during_history_navigation");
+ let pageshowCount = 0;
+ bc.onmessage = function(event) {
+ if (event.data.type == "pageshow") {
+ ++pageshowCount;
+ info("pageshow: " + pageshowCount + ", page: " + event.data.page);
+ if (pageshowCount == 1) {
+ ok(!event.data.persisted, "The page should not be bfcached.");
+ bc.postMessage("loadnext");
+ } else if (pageshowCount == 2) {
+ ok(!event.data.persisted, "The page should not be bfcached.");
+ bc.postMessage("loadnext");
+ } else if (pageshowCount == 3) {
+ ok(!event.data.persisted, "The page should not be bfcached.");
+ bc.postMessage("back");
+ } else if (pageshowCount == 4) {
+ ok(event.data.persisted, "The page should be bfcached.");
+ bc.postMessage("forward");
+ } else if (pageshowCount == 5) {
+ ok(event.data.page.includes("v2"), "Should have gone forward.");
+ bc.postMessage("close");
+ SimpleTest.finish();
+ }
+ }
+ };
+ win = window.open("file_new_shentry_during_history_navigation_3.html", "", "noopener");
+
+ }
+
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_not-opener.html b/docshell/test/navigation/test_not-opener.html
new file mode 100644
index 0000000000..acdb9473e6
--- /dev/null
+++ b/docshell/test/navigation/test_not-opener.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+if (!navigator.platform.startsWith("Win")) {
+ SimpleTest.expectAssertions(0, 1);
+}
+
+window.onload = async function() {
+ // navigateByLocation(window0); // Don't have a handle to the window.
+ navigateByOpen("window1");
+ navigateByForm("window2");
+ navigateByHyperlink("window3");
+
+ await waitForFinishedFrames(6);
+
+ is((await getFramesByName("window1")).length, 2, "Should not be able to navigate popup's popup by calling window.open.");
+ is((await getFramesByName("window2")).length, 2, "Should not be able to navigate popup's popup by submitting form.");
+ is((await getFramesByName("window3")).length, 2, "Should not be able to navigate popup's popup by targeted hyperlink.");
+
+ // opener0.close();
+ opener1.close();
+ opener2.close();
+ opener3.close();
+
+ info("here")
+ await cleanupWindows();
+ info("there")
+ SimpleTest.finish();
+};
+
+// opener0 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/open.html#window0", "_blank", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+let opener1 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/open.html#window1", "_blank", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+let opener2 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/open.html#window2", "_blank", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+let opener3 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/open.html#window3", "_blank", "width=10,height=10");
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_online_offline_bfcache.html b/docshell/test/navigation/test_online_offline_bfcache.html
new file mode 100644
index 0000000000..4ad90fd52e
--- /dev/null
+++ b/docshell/test/navigation/test_online_offline_bfcache.html
@@ -0,0 +1,101 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Online/Offline with BFCache</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+
+ /*
+ * The test is designed to work with and without bfcache.
+ * (1) First the test opens a window which then loads another page which
+ * goes back to the original page to detect if bfcache is enabled. If
+ * bfcache isn't enabled, close message is sent to the opened window and it
+ * closes itself and sends a message back and the test finishes.
+ * (2) The browser is set to offline mode. The opened page sends message
+ * that it has received offline event. This controller page then asks the
+ * page to go forward. The page which comes out from the bfcache gets
+ * offline event and sends message about that to this controller.
+ * (3) Browser is set to online mode. Similar cycle as with offline happens.
+ * (4) Controller page sends close message to the opened window and it
+ * closes itself and sends a message back and the test finishes.
+ */
+
+ function offlineOnline(online) {
+ function offlineFn() {
+ /* eslint-env mozilla/chrome-script */
+ Services.io.offline = true;
+ }
+ function onlineFn() {
+ /* eslint-env mozilla/chrome-script */
+ Services.io.offline = false;
+ }
+ SpecialPowers.loadChromeScript(online ? onlineFn : offlineFn);
+ }
+
+ var bc = new BroadcastChannel("online_offline_bfcache");
+ var pageshowCount = 0;
+ var offlineCount = 0;
+ var onlineCount = 0;
+
+ bc.onmessage = function(event) {
+ if (event.data.event == "pageshow") {
+ ++pageshowCount;
+ info("pageshow " + pageshowCount);
+ if (pageshowCount == 1) {
+ ok(!event.data.persisted);
+ bc.postMessage("nextpage");
+ } else if (pageshowCount == 2) {
+ ok(!event.data.persisted);
+ bc.postMessage("back");
+ } else if (pageshowCount == 3) {
+ if (!event.data.persisted) {
+ info("BFCache is not enabled, return early");
+ bc.postMessage("close");
+ } else {
+ offlineOnline(false);
+ }
+ }
+ } else if (event.data == "offline") {
+ ++offlineCount;
+ info("offline " + offlineCount);
+ if (offlineCount == 1) {
+ bc.postMessage("forward");
+ } else if (offlineCount == 2) {
+ offlineOnline(true);
+ } else {
+ ok(false, "unexpected offline event");
+ }
+ } else if (event.data == "online") {
+ ++onlineCount;
+ info("online " + onlineCount);
+ if (onlineCount == 1) {
+ bc.postMessage("back");
+ } else if (onlineCount == 2) {
+ bc.postMessage("close");
+ } else {
+ ok(false, "unexpected online event");
+ }
+ } else if ("closed") {
+ ok(true, "Did pass the test");
+ bc.close();
+ SimpleTest.finish();
+ }
+ };
+
+ function runTest() {
+ SpecialPowers.pushPrefEnv({"set": [["network.manage-offline-status", false]]}, function() {
+ window.open("file_online_offline_bfcache.html", "", "noopener");
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+</head>
+<body onload="runTest()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_open_javascript_noopener.html b/docshell/test/navigation/test_open_javascript_noopener.html
new file mode 100644
index 0000000000..81a6b70d61
--- /dev/null
+++ b/docshell/test/navigation/test_open_javascript_noopener.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <script>
+
+add_task(async function test_open_javascript_noopener() {
+ const topic = "test-javascript-was-run";
+ function jsuri(version) {
+ return `javascript:SpecialPowers.notifyObservers(null, "${topic}", "${version}");window.close()`;
+ }
+
+ let seen = [];
+ function observer(_subject, _topic, data) {
+ info(`got notification ${data}`);
+ seen.push(data);
+ }
+ SpecialPowers.addObserver(observer, topic);
+
+ isDeeply(seen, [], "seen no test notifications");
+ window.open(jsuri("1"));
+
+ // Bounce off the parent process to make sure the JS will have run.
+ await SpecialPowers.spawnChrome([], () => {});
+
+ isDeeply(seen, ["1"], "seen the opener notification");
+
+ window.open(jsuri("2"), "", "noopener");
+
+ // Bounce off the parent process to make sure the JS will have run.
+ await SpecialPowers.spawnChrome([], () => {});
+
+ isDeeply(seen, ["1"], "didn't get a notification from the noopener popup");
+
+ SpecialPowers.removeObserver(observer, topic);
+});
+
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/test_opener.html b/docshell/test/navigation/test_opener.html
new file mode 100644
index 0000000000..ce966b897d
--- /dev/null
+++ b/docshell/test/navigation/test_opener.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+if (navigator.platform.startsWith("Linux")) {
+ SimpleTest.expectAssertions(0, 1);
+}
+
+window.onload = async function() {
+ navigateByLocation(window0);
+ navigateByOpen("window1");
+ navigateByForm("window2");
+ navigateByHyperlink("window3");
+
+ await waitForFinishedFrames(4);
+ await isNavigated(window0, "Should be able to navigate popup by setting location.");
+ await isNavigated(window1, "Should be able to navigate popup by calling window.open.");
+ await isNavigated(window2, "Should be able to navigate popup by submitting form.");
+ await isNavigated(window3, "Should be able to navigate popup by targeted hyperlink.");
+
+ window0.close();
+ window1.close();
+ window2.close();
+ window3.close();
+
+ await cleanupWindows();
+
+ SimpleTest.finish();
+};
+
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window0 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/blank.html", "window0", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window1 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/blank.html", "window1", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window2 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/blank.html", "window2", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window3 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/blank.html", "window3", "width=10,height=10");
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_performance_navigation.html b/docshell/test/navigation/test_performance_navigation.html
new file mode 100644
index 0000000000..75abbdd767
--- /dev/null
+++ b/docshell/test/navigation/test_performance_navigation.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=145971
+-->
+<head>
+ <title>Test for Bug 145971</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=145971">Mozilla Bug 145971</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+var testWindow;
+var bc = new BroadcastChannel("bug145971");
+bc.onmessage = function(msgEvent) {
+ var result = msgEvent.data.result;
+ if (result == undefined) {
+ info("Got unexpected message from BroadcastChannel");
+ return;
+ }
+ ok(result, "Bug 145971: Navigation type does not equal 2 when restoring document from session history.");
+ SimpleTest.finish();
+};
+
+function runTest() {
+ // If Fission is disabled, the pref is no-op.
+ SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ window.open("test_bug145971.html", "", "width=360,height=480,noopener");
+ });
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_popup-navigates-children.html b/docshell/test/navigation/test_popup-navigates-children.html
new file mode 100644
index 0000000000..82d69e7982
--- /dev/null
+++ b/docshell/test/navigation/test_popup-navigates-children.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+
+let window0 = null;
+let window1 = null;
+let window2 = null;
+let window3 = null;
+
+function testChild0() {
+ if (!window.window0)
+ window0 = window.open("navigate.html#opener.frames[0],location", "window0", "width=10,height=10");
+}
+
+function testChild1() {
+ if (!window.window1)
+ window1 = window.open("navigate.html#child1,open", "window1", "width=10,height=10");
+}
+
+function testChild2() {
+ if (!window.window2)
+ window2 = window.open("navigate.html#child2,form", "window2", "width=10,height=10");
+}
+
+function testChild3() {
+ if (!window.window3)
+ window3 = window.open("navigate.html#child3,hyperlink", "window3", "width=10,height=10");
+}
+
+window.onload = async function() {
+ await waitForFinishedFrames(4);
+ await isNavigated(frames[0], "Should be able to navigate on-domain opener's children by setting location.");
+ await isNavigated(frames[1], "Should be able to navigate on-domain opener's children by calling window.open.");
+ await isNavigated(frames[2], "Should be able to navigate on-domain opener's children by submitting form.");
+ await isNavigated(frames[3], "Should be able to navigate on-domain opener's children by targeted hyperlink.");
+
+ window0.close();
+ window1.close();
+ window2.close();
+ window3.close();
+
+ await cleanupWindows();
+ SimpleTest.finish();
+};
+
+</script>
+</head>
+<body>
+<div id="frames">
+<iframe onload="testChild0()" name="child0" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe onload="testChild1()" name="child1" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe onload="testChild2()" name="child2" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe onload="testChild3()" name="child3" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_rate_limit_location_change.html b/docshell/test/navigation/test_rate_limit_location_change.html
new file mode 100644
index 0000000000..b1b51b92dd
--- /dev/null
+++ b/docshell/test/navigation/test_rate_limit_location_change.html
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1314912
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1314912</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1314912 */
+
+ const RATE_LIMIT_COUNT = 90;
+ const RATE_LIMIT_TIME_SPAN = 3;
+
+ async function setup() {
+ await SpecialPowers.pushPrefEnv({set: [
+ ["dom.navigation.locationChangeRateLimit.count", RATE_LIMIT_COUNT],
+ ["dom.navigation.locationChangeRateLimit.timespan", RATE_LIMIT_TIME_SPAN]]});
+ }
+
+ let inc = 0;
+
+ const rateLimitedFunctions = (win) => ({
+ "history.replaceState": () => win.history.replaceState(null, "test", `${win.location.href}#${inc++}`),
+ "history.pushState": () => win.history.pushState(null, "test", `${win.location.href}#${inc++}`),
+ "history.back": () => win.history.back(),
+ "history.forward": () => win.history.forward(),
+ "history.go": () => win.history.go(-1),
+ "location.href": () => win.location.href = win.location.href + "",
+ "location.hash": () => win.location.hash = inc++,
+ "location.host": () => win.location.host = win.location.host + "",
+ "location.hostname": () => win.location.hostname = win.location.hostname + "",
+ "location.pathname": () => win.location.pathname = win.location.pathname + "",
+ "location.port": () => win.location.port = win.location.port + "",
+ "location.protocol": () => win.location.protocol = win.location.protocol + "",
+ "location.search": () => win.location.search = win.location.search + "",
+ "location.assign": () => win.location.assign(`${win.location.href}#${inc++}`),
+ "location.replace": () => win.location.replace(`${win.location.href}#${inc++}`),
+ "location.reload": () => win.location.reload(),
+ });
+
+ async function test() {
+ await setup();
+
+ // Open new window and wait for it to load
+ let win = window.open("blank.html");
+ await new Promise((resolve) => SimpleTest.waitForFocus(resolve, win))
+
+ // Execute the history and location functions
+ Object.entries(rateLimitedFunctions(win)).forEach(([name, fn]) => {
+ // Reset the rate limit for the next run.
+ info("Reset rate limit.");
+ SpecialPowers.wrap(win).browsingContext.resetLocationChangeRateLimit();
+
+ info(`Calling ${name} ${RATE_LIMIT_COUNT} times to reach the rate limit.`);
+ for(let i = 0; i< RATE_LIMIT_COUNT; i++) {
+ fn.call(this);
+ }
+ // Next calls should throw because we're above the rate limit
+ for(let i = 0; i < 5; i++) {
+ SimpleTest.doesThrow(() => fn.call(this), `Call #${RATE_LIMIT_COUNT + i + 1} to ${name} should throw.`);
+ }
+ })
+
+ // We didn't reset the rate limit after the last loop iteration above.
+ // Wait for the rate limit timer to expire.
+ SimpleTest.requestFlakyTimeout("Waiting to trigger rate limit reset.");
+ await new Promise((resolve) => setTimeout(resolve, 5000));
+
+ // Calls should be allowed again.
+ Object.entries(rateLimitedFunctions(win)).forEach(([name, fn]) => {
+ let didThrow = false;
+ try {
+ fn.call(this);
+ } catch(error) {
+ didThrow = true;
+ }
+ is(didThrow, false, `Call to ${name} must not throw.`)
+ });
+
+ // Cleanup
+ win.close();
+ SpecialPowers.wrap(win).browsingContext.resetLocationChangeRateLimit();
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body onload="setTimeout(test, 0);">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1314912">Mozilla Bug 1314912</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_recursive_frames.html b/docshell/test/navigation/test_recursive_frames.html
new file mode 100644
index 0000000000..3ccc09dd14
--- /dev/null
+++ b/docshell/test/navigation/test_recursive_frames.html
@@ -0,0 +1,167 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Recursive Loads</title>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1597427">Mozilla Bug 1597427</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ const TEST_CASES = [
+ { // too many recursive iframes
+ frameId: "recursiveFrame",
+ expectedLocations: [
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_recursive.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_recursive.html",
+ "about:blank",
+ ],
+ },
+ { // too many recursive iframes
+ frameId: "twoRecursiveIframes",
+ expectedLocations: [
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_load_as_example_com.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.org/tests/docshell/test/navigation/frame_load_as_example_org.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_load_as_example_com.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.org/tests/docshell/test/navigation/frame_load_as_example_org.html",
+ "about:blank",
+ ],
+ },
+ { // too many recursive iframes
+ frameId: "threeRecursiveIframes",
+ expectedLocations: [
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://sub1.test1.mochi.test:8888/tests/docshell/test/navigation/frame_load_as_host1.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_load_as_host2.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://test1.mochi.test:8888/tests/docshell/test/navigation/frame_load_as_host3.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://sub1.test1.mochi.test:8888/tests/docshell/test/navigation/frame_load_as_host1.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_load_as_host2.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://test1.mochi.test:8888/tests/docshell/test/navigation/frame_load_as_host3.html",
+ "about:blank",
+ ],
+ },
+ { // too many nested iframes
+ frameId: "sixRecursiveIframes",
+ expectedLocations: [
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_1_out_of_6.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://test1.mochi.test:8888/tests/docshell/test/navigation/frame_2_out_of_6.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://sub1.test1.mochi.test:8888/tests/docshell/test/navigation/frame_3_out_of_6.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://sub2.xn--lt-uia.mochi.test:8888/tests/docshell/test/navigation/frame_4_out_of_6.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://test2.mochi.test:8888/tests/docshell/test/navigation/frame_5_out_of_6.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.org/tests/docshell/test/navigation/frame_6_out_of_6.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_1_out_of_6.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://test1.mochi.test:8888/tests/docshell/test/navigation/frame_2_out_of_6.html",
+ ],
+ },
+ { // too many recursive objects
+ frameId: "recursiveObject",
+ expectedLocations: [
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://sub2.xn--lt-uia.mochi.test:8888/tests/docshell/test/navigation/object_recursive_load.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://sub2.xn--lt-uia.mochi.test:8888/tests/docshell/test/navigation/object_recursive_load.html",
+ ],
+ },
+ { // 3 nested srcdocs, should show all of them
+ frameId: "nestedSrcdoc",
+ expectedLocations: [
+ "about:srcdoc",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/file_nested_srcdoc.html",
+ "about:srcdoc",
+ "about:srcdoc",
+ ],
+ },
+ ];
+
+ async function checkRecursiveLoad(level) {
+ let el = content.document.getElementById("static");
+ let documentURI = await SpecialPowers.spawn(
+ el,
+ [],
+ () => this.content.document.documentURI
+ );
+ if (documentURI == "about:blank") {
+ // If we had too many recursive frames, the most inner iframe's uri will be about:blank
+ return [documentURI];
+ }
+ if (documentURI == "about:srcdoc" && level == 3) {
+ // Check that we have the correct most inner srcdoc iframe
+ let innerText = await SpecialPowers.spawn(
+ el,
+ [],
+ () => this.content.document.body.innerText
+ );
+ is(innerText, "Third nested srcdoc", "correct most inner srcdoc iframe");
+ }
+ let nestedIfrOrObjectURI = [];
+ try {
+ // Throws an error when we have too many nested frames/objects, because we
+ // claim to have no content window for the inner most frame/object.
+ nestedIfrOrObjectURI = await SpecialPowers.spawn(
+ el,
+ [level + 1],
+ checkRecursiveLoad
+ );
+ } catch (err) {
+ info(
+ `Tried to spawn another task in the iframe/object, but got err: ${err}, must have had too many nested iframes/objects\n`
+ );
+ }
+ return [documentURI, ...nestedIfrOrObjectURI];
+ }
+
+ add_task(async () => {
+ for (const testCase of TEST_CASES) {
+ let el = document.getElementById(testCase.frameId);
+ let loc = await SpecialPowers.spawn(
+ el,
+ [],
+ () => this.content.location.href
+ );
+ let locations = await SpecialPowers.spawn(el, [1], checkRecursiveLoad);
+ isDeeply(
+ [loc, ...locations],
+ testCase.expectedLocations,
+ "iframes/object loaded in correct order"
+ );
+ }
+ });
+
+ </script>
+</pre>
+<div>
+ <iframe style="height: 100vh; width:25%;" id="recursiveFrame" src="http://example.com/tests/docshell/test/navigation/frame_recursive.html"></iframe>
+ <iframe style="height: 100vh; width:25%;" id="twoRecursiveIframes" src="http://example.com/tests/docshell/test/navigation/frame_load_as_example_com.html"></iframe>
+ <iframe style="height: 100vh; width:25%;" id="threeRecursiveIframes" src="http://sub1.test1.mochi.test:8888/tests/docshell/test/navigation/frame_load_as_host1.html"></iframe>
+ <iframe style="height: 100vh; width:25%;" id="sixRecursiveIframes" src="http://example.com/tests/docshell/test/navigation/frame_1_out_of_6.html"></iframe>
+ <object width="400" height="300" id="recursiveObject" data="http://sub2.xn--lt-uia.mochi.test:8888/tests/docshell/test/navigation/object_recursive_load.html"></object>
+ <iframe id="nestedSrcdoc" srcdoc="Srcdoc that will embed an iframe &lt;iframe id=&quot;static&quot; src=&quot;http://example.com/tests/docshell/test/navigation/file_nested_srcdoc.html&quot;&gt;&lt;/iframe&gt;"></iframe>
+</div>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_reload.html b/docshell/test/navigation/test_reload.html
new file mode 100644
index 0000000000..7e75c7c035
--- /dev/null
+++ b/docshell/test/navigation/test_reload.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Ensure a page which is otherwise bfcacheable doesn't crash on reload</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ let pageshowCount = 0;
+ let bc = new BroadcastChannel("test_reload");
+ bc.onmessage = function(event) {
+ info("MessageEvent: " + event.data);
+ if (event.data == "pageshow") {
+ ++pageshowCount;
+ info("pageshow: " + pageshowCount);
+ if (pageshowCount < 3) {
+ info("Sending reload");
+ bc.postMessage("reload");
+ } else {
+ info("Sending close");
+ bc.postMessage("close");
+ }
+ } else if (event.data == "closed") {
+ info("closed");
+ bc.close();
+ ok(true, "Passed");
+ SimpleTest.finish();
+ }
+ }
+
+ function test() {
+ window.open("file_reload.html", "", "noopener");
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_reload_large_postdata.html b/docshell/test/navigation/test_reload_large_postdata.html
new file mode 100644
index 0000000000..15fae33ac3
--- /dev/null
+++ b/docshell/test/navigation/test_reload_large_postdata.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+
+<form id="form" action="file_reload_large_postdata.sjs" target="_blank" rel="opener" method="POST">
+ <input id="input" name="payload" type="hidden" value=""/>
+</form>
+
+<pre id="test">
+<script>
+// This is derived from `kTooLargeStream` in `IPCStreamUtils.cpp`.
+const kTooLargeStream = 1024 * 1024;
+
+function waitForPopup(expected) {
+ return new Promise(resolve => {
+ addEventListener("message", evt => {
+ info("got message!");
+ is(evt.source.opener, window, "the event source's opener should be this window");
+ is(evt.data, expected, "got the expected data from the popup");
+ resolve(evt.source);
+ }, { once: true });
+ });
+}
+
+add_task(async function() {
+ await SpecialPowers.pushPrefEnv({"set": [["dom.confirm_repost.testing.always_accept", true]]});
+ let form = document.getElementById("form");
+ let input = document.getElementById("input");
+
+ // Create a very large value to include in the post payload. This should
+ // ensure that the value isn't sent directly over IPC, and is instead sent as
+ // an async inputstream.
+ let payloadSize = kTooLargeStream;
+
+ let popupReady = waitForPopup(payloadSize);
+ input.value = "A".repeat(payloadSize);
+ form.submit();
+
+ let popup = await popupReady;
+ try {
+ let popupReady2 = waitForPopup(payloadSize);
+ info("reloading popup");
+ popup.location.reload();
+ let popup2 = await popupReady2;
+ is(popup, popup2);
+ } finally {
+ popup.close();
+ }
+});
+
+// The .sjs server can time out processing the 1mb payload in debug builds.
+SimpleTest.requestLongerTimeout(2);
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_reload_nonbfcached_srcdoc.html b/docshell/test/navigation/test_reload_nonbfcached_srcdoc.html
new file mode 100644
index 0000000000..2399a0ad7d
--- /dev/null
+++ b/docshell/test/navigation/test_reload_nonbfcached_srcdoc.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test srcdoc handling when reloading a page.</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ // The old session history implementation asserts in
+ // https://searchfox.org/mozilla-central/rev/b822a27de3947d3f4898defac6164e52caf1451b/docshell/shistory/nsSHEntry.cpp#670-672
+ SimpleTest.expectAssertions(0, 1);
+ SimpleTest.waitForExplicitFinish();
+
+ var win;
+ function test() {
+ window.onmessage = function(event) {
+ if (event.data == "pageload:") {
+ // Trigger a similar reload as what the reload button does.
+ SpecialPowers.wrap(win)
+ .docShell
+ .QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
+ .sessionHistory
+ .reload(0);
+ } else if (event.data == "pageload:second") {
+ ok(true, "srcdoc iframe was updated.");
+ win.close();
+ SimpleTest.finish();
+ }
+ }
+ win = window.open("file_reload_nonbfcached_srcdoc.sjs");
+ }
+
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_reserved.html b/docshell/test/navigation/test_reserved.html
new file mode 100644
index 0000000000..0242f3941b
--- /dev/null
+++ b/docshell/test/navigation/test_reserved.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 200px; }
+ </style>
+<script>
+if (navigator.platform.startsWith("Mac")) {
+ SimpleTest.expectAssertions(0, 2);
+}
+
+async function testTop() {
+ let window0 = window.open("iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#top,location", "_blank", "width=10,height=10");
+
+ await waitForFinishedFrames(1);
+ isInaccessible(window0, "Should be able to navigate off-domain top by setting location.");
+ window0.close();
+ await cleanupWindows();
+
+ let window1 = window.open("iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#_top,open", "_blank", "width=10,height=10");
+
+ await waitForFinishedFrames(1);
+ isInaccessible(window1, "Should be able to navigate off-domain top by calling window.open.");
+ window1.close();
+ await cleanupWindows();
+
+ let window2 = window.open("iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#_top,form", "_blank", "width=10,height=10");
+
+ await waitForFinishedFrames(1);
+ isInaccessible(window2, "Should be able to navigate off-domain top by submitting form.");
+ window2.close();
+ await cleanupWindows();
+
+ let window3 = window.open("iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#_top,hyperlink", "_blank", "width=10,height=10");
+
+ await waitForFinishedFrames(1);
+ isInaccessible(window3, "Should be able to navigate off-domain top by targeted hyperlink.");
+ window3.close();
+ await cleanupWindows();
+
+ await testParent();
+}
+
+async function testParent() {
+ document.getElementById("frames").innerHTML = '<iframe src="iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#parent,location"></iframe>';
+
+ await waitForFinishedFrames(1);
+ isAccessible(frames[0], "Should not be able to navigate off-domain parent by setting location.");
+ await cleanupWindows();
+
+ document.getElementById("frames").innerHTML = '<iframe src="iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#_parent,open"></iframe>';
+
+ await waitForFinishedFrames(1);
+ isAccessible(frames[0], "Should not be able to navigate off-domain parent by calling window.open.");
+ await cleanupWindows();
+
+ document.getElementById("frames").innerHTML = '<iframe src="iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#_parent,form"></iframe>';
+
+ await waitForFinishedFrames(1);
+ isAccessible(frames[0], "Should not be able to navigate off-domain parent by submitting form.");
+ await cleanupWindows();
+
+ document.getElementById("frames").innerHTML = '<iframe src="iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#_parent,hyperlink"></iframe>';
+
+ await waitForFinishedFrames(1);
+ isAccessible(frames[0], "Should not be able to navigate off-domain parent by targeted hyperlink.");
+ await cleanupWindows();
+
+ document.getElementById("frames").innerHTML = "";
+ SimpleTest.finish();
+}
+
+window.onload = async function() {
+ await testTop();
+};
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
+<div id="frames">
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_same_url.html b/docshell/test/navigation/test_same_url.html
new file mode 100644
index 0000000000..820caa7005
--- /dev/null
+++ b/docshell/test/navigation/test_same_url.html
@@ -0,0 +1,56 @@
+
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="application/javascript" src="/tests/SimpleTest/SpecialPowers.js"></script>
+ <script type="application/javascript">
+ // Since BFCache in parent requires no opener, use BroadcastChannel
+ // to communicate with file_same_url.html.
+ let bc = new BroadcastChannel("test_same_url");
+ async function test() {
+ var promise;
+ let historyLength;
+
+ promise = waitForLoad();
+ window.open("file_same_url.html", "_blank", "noopener=yes");
+ historyLength = await promise;
+ is(historyLength, 1, "before same page navigation");
+
+ promise = waitForLoad();
+ bc.postMessage("linkClick");
+ historyLength = await promise;
+ is(historyLength, 1, "after same page navigation");
+ bc.postMessage("closeWin");
+
+ SimpleTest.finish();
+ }
+
+ async function waitForLoad() {
+ return new Promise(resolve => {
+ let listener = e => {
+ if (e.data.bodyOnLoad) {
+ bc.removeEventListener("message", listener);
+ setTimeout(() => resolve(e.data.bodyOnLoad), 0);
+ }
+ };
+ bc.addEventListener("message", listener);
+ });
+ }
+ </script>
+</head>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1745730">Bug 1745730</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+<body onload="test()">
+</body>
+</html>
+
diff --git a/docshell/test/navigation/test_scrollRestoration.html b/docshell/test/navigation/test_scrollRestoration.html
new file mode 100644
index 0000000000..d31598f391
--- /dev/null
+++ b/docshell/test/navigation/test_scrollRestoration.html
@@ -0,0 +1,214 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 1155730</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1155730">Mozilla Bug 1155730</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+
+ function assertCheck(data) {
+ if (data.assertIs) {
+ for (const args of data.assertIs) {
+ is(args[0], args[1], args[2]);
+ }
+ }
+ if (data.assertOk) {
+ for (const args of data.assertOk) {
+ ok(args[0], args[1]);
+ }
+ }
+ if (data.assertIsNot) {
+ for (const args of data.assertIsNot) {
+ isnot(args[0], args[1], args[2]);
+ }
+ }
+ }
+
+ var bc1, currentCase = 0;
+ function test1() {
+ bc1 = new BroadcastChannel("bug1155730_part1");
+ bc1.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "pageshow") {
+ currentCase++;
+ var persisted = msg.persisted;
+ is(persisted, false, "Shouldn't have persisted session history entry.");
+ bc1.postMessage({command: "test", currentCase});
+ } else if (command == "asserts") {
+ is(msg.currentCase, currentCase, "correct case");
+ info(`Checking asserts for case ${msg.currentCase}`);
+ assertCheck(msg);
+ if (currentCase == 3) {
+ // move on to the next test
+ bc1.close();
+ test2();
+ }
+ }
+ }
+ window.open("file_scrollRestoration_part1_nobfcache.html", "", "width=360,height=480,noopener");
+ }
+
+ var bc2, bc2navigate;
+ function test2() {
+ currentCase = 0;
+ bc2 = new BroadcastChannel("bug1155730_part2");
+ bc2.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "pageshow") {
+ currentCase++;
+ var persisted = msg.persisted;
+ switch (currentCase) {
+ case 1:
+ is(persisted, false, "Shouldn't have persisted session history entry.");
+ break;
+ case 2:
+ is(persisted, true, "Should have persisted session history entry.");
+ }
+ bc2.postMessage({command: "test", currentCase});
+ } else if (command == "asserts") {
+ is(msg.currentCase, currentCase, "correct case");
+ info(`Checking asserts for case ${msg.currentCase}`);
+ assertCheck(msg);
+ if (currentCase == 3) {
+ // move on to the next test
+ bc2.close();
+ test3();
+ }
+ } else if (command == "nextCase") {
+ currentCase++;
+ }
+ }
+
+ bc2navigate = new BroadcastChannel("navigate");
+ bc2navigate.onmessage = (event) => {
+ if (event.data.command == "loaded") {
+ bc2navigate.postMessage({command: "back"})
+ bc2navigate.close();
+ }
+ }
+ window.open("file_scrollRestoration_part2_bfcache.html", "", "width=360,height=480,noopener");
+ }
+
+ var bc3, bc3navigate;
+ function test3() {
+ currentCase = 0;
+ bc3 = new BroadcastChannel("bug1155730_part3");
+ bc3.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "pageshow") {
+ currentCase++;
+ if (currentCase == 3) {
+ var persisted = msg.persisted;
+ is(persisted, false, "Shouldn't have persisted session history entry.");
+ }
+
+ bc3.postMessage({command: "test", currentCase});
+ } else if (command == "asserts") {
+ is(msg.currentCase, currentCase, "correct case");
+ info(`Checking asserts for case ${msg.currentCase}`);
+ assertCheck(msg);
+ } else if (command == "nextCase") {
+ currentCase++;
+ } else if (command == "finishing") {
+ bc3.close();
+ test4();
+ }
+ }
+
+ bc3navigate = new BroadcastChannel("navigate");
+ bc3navigate.onmessage = (event) => {
+ if (event.data.command == "loaded") {
+ is(event.data.scrollRestoration, 'auto', "correct scroll restoration");
+ bc3navigate.postMessage({command: "back"})
+ bc3navigate.close();
+ }
+ }
+ window.open("file_scrollRestoration_part3_nobfcache.html", "", "width=360,height=480,noopener");
+ }
+
+ // test4 opens a new page which can enter bfcache. That page then loads
+ // another page which can't enter bfcache. That second page then scrolls
+ // down. History API is then used to navigate back and forward. When the
+ // second page loads again, it should scroll down automatically.
+ var bc4a, bc4b;
+ var scrollYCounter = 0;
+ function test4() {
+ currentCase = 0;
+ bc4a = new BroadcastChannel("bfcached");
+ bc4a.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "pageshow") {
+ ++currentCase;
+ if (currentCase == 1) {
+ ok(!msg.persisted, "The first page should not be persisted initially.");
+ bc4a.postMessage("loadNext");
+ } else if (currentCase == 3) {
+ ok(msg.persisted, "The first page should be persisted.");
+ bc4a.postMessage("forward");
+ bc4a.close();
+ }
+ }
+ }
+
+ bc4b = new BroadcastChannel("notbfcached");
+ bc4b.onmessage = (event) => {
+ var msg = event.data;
+ var command = msg.command;
+ if (command == "pageshow") {
+ ++currentCase;
+ if (currentCase == 2) {
+ ok(!msg.persisted, "The second page should not be persisted.");
+ bc4b.postMessage("getScrollY");
+ bc4b.postMessage("scroll");
+ bc4b.postMessage("getScrollY");
+ bc4b.postMessage("back");
+ } else if (currentCase == 4) {
+ ok(!msg.persisted, "The second page should not be persisted.");
+ bc4b.postMessage("getScrollY");
+ }
+ } else if (msg == "closed") {
+ bc4b.close();
+ SimpleTest.finish();
+ } else if ("scrollY" in msg) {
+ ++scrollYCounter;
+ if (scrollYCounter == 1) {
+ is(msg.scrollY, 0, "The page should be initially scrolled to top.");
+ } else if (scrollYCounter == 2) {
+ isnot(msg.scrollY, 0, "The page should be then scrolled down.");
+ } else if (scrollYCounter == 3) {
+ isnot(msg.scrollY, 0, "The page should be scrolled down after being restored from the session history.");
+ bc4b.postMessage("close");
+ }
+ }
+ }
+ window.open("file_scrollRestoration_bfcache_and_nobfcache.html", "", "width=360,height=480,noopener");
+ }
+
+ function runTest() {
+ // If Fission is disabled, the pref is no-op.
+ SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ test1();
+ });
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_session_history_entry_cleanup.html b/docshell/test/navigation/test_session_history_entry_cleanup.html
new file mode 100644
index 0000000000..a55de0d6c3
--- /dev/null
+++ b/docshell/test/navigation/test_session_history_entry_cleanup.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 534178</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=534178">Mozilla Bug 534178</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_bug534178.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_session_history_on_redirect.html b/docshell/test/navigation/test_session_history_on_redirect.html
new file mode 100644
index 0000000000..a303f81536
--- /dev/null
+++ b/docshell/test/navigation/test_session_history_on_redirect.html
@@ -0,0 +1,92 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Session history on redirect</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ /*
+ * The test opens a new window and loads a page there. Then another document
+ * is loaded to the window. The initial load of that second page doesn't do
+ * a redirect. Now another non-redirecting page is loaded. Then
+ * history.go(-2) and history.forward() are called. The second time
+ * the second page is loaded, it does a redirect. history.back() and
+ * history.forward() are called again. The page which did the redirect
+ * shouldn't be accessed, but the page which it redirected to.
+ * Finally history.forward() is called again and the third page should be
+ * loaded and history.length should have the same value as it had when the
+ * third page was loaded the first time.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+ var win;
+ var finalHistoryLength = 0;
+
+ function run() {
+ win = window.open("file_session_history_on_redirect.html");
+ }
+
+ var pageshowCounter = 0;
+ async function pageshow() {
+ // Need to trigger new loads asynchronously after page load, otherwise
+ // new loads are treated as replace loads.
+ await new Promise((r) => setTimeout(r));
+ ++pageshowCounter;
+ info("Page load: " + win.location.href);
+ switch (pageshowCounter) {
+ case 1:
+ ok(win.location.href.includes("file_session_history_on_redirect.html"));
+ win.location.href = "redirect_handlers.sjs";
+ break;
+ case 2:
+ ok(win.location.href.includes("redirect_handlers.sjs"));
+ // Put the initial page also as the last entry in the session history.
+ win.location.href = "file_session_history_on_redirect.html";
+ break;
+ case 3:
+ ok(win.location.href.includes("file_session_history_on_redirect.html"));
+ finalHistoryLength = win.history.length;
+ win.history.go(-2);
+ break;
+ case 4:
+ ok(win.location.href.includes("file_session_history_on_redirect.html"));
+ win.history.forward();
+ break;
+ case 5:
+ ok(win.location.href.includes("file_session_history_on_redirect_2.html"));
+ win.history.back();
+ break;
+ case 6:
+ ok(win.location.href.includes("file_session_history_on_redirect.html"));
+ win.history.forward();
+ break;
+ case 7:
+ ok(win.location.href.includes("file_session_history_on_redirect_2.html"));
+ is(win.history.length, finalHistoryLength, "Shouldn't have changed the history length.");
+ win.history.forward();
+ break;
+ case 8:
+ ok(win.location.href.includes("file_session_history_on_redirect.html"));
+ is(win.history.length, finalHistoryLength, "Shouldn't have changed the history length.");
+ win.onpagehide = null;
+ finishTest();
+ break;
+ default:
+ ok(false, "unexpected pageshow");
+ }
+ }
+
+ function finishTest() {
+ win.close()
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body onload="run()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_sessionhistory.html b/docshell/test/navigation/test_sessionhistory.html
new file mode 100644
index 0000000000..2254ec876b
--- /dev/null
+++ b/docshell/test/navigation/test_sessionhistory.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 462076</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="nextTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=462076">Mozilla Bug 462076</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ var testFiles =
+ [ "file_bug462076_1.html", // Dynamic frames before onload
+ "file_bug462076_2.html", // Dynamic frames when handling onload
+ "file_bug462076_3.html", // Dynamic frames after onload
+ ];
+ var testCount = 0; // Used by the test files.
+
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function nextTest_() {
+ if (testFiles.length) {
+ testCount = 0;
+ let nextFile = testFiles.shift();
+ info("Running " + nextFile);
+ testWindow = window.open(nextFile, "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ } else {
+ SimpleTest.finish();
+ }
+ }
+
+ function nextTest() {
+ setTimeout(nextTest_, 0);
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_sessionhistory_document_write.html b/docshell/test/navigation/test_sessionhistory_document_write.html
new file mode 100644
index 0000000000..2a48a8154e
--- /dev/null
+++ b/docshell/test/navigation/test_sessionhistory_document_write.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Session history + document.write</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_document_write_1.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_sessionhistory_iframe_removal.html b/docshell/test/navigation/test_sessionhistory_iframe_removal.html
new file mode 100644
index 0000000000..242e3baade
--- /dev/null
+++ b/docshell/test/navigation/test_sessionhistory_iframe_removal.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Session history + document.write</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_sessionhistory_iframe_removal.html", "", "width=360,height=480");
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_shiftReload_and_pushState.html b/docshell/test/navigation/test_shiftReload_and_pushState.html
new file mode 100644
index 0000000000..7525e2e21f
--- /dev/null
+++ b/docshell/test/navigation/test_shiftReload_and_pushState.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 1003100</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1003100">Mozilla Bug 1003100</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_shiftReload_and_pushState.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_ship_beforeunload_fired.html b/docshell/test/navigation/test_ship_beforeunload_fired.html
new file mode 100644
index 0000000000..e43711676b
--- /dev/null
+++ b/docshell/test/navigation/test_ship_beforeunload_fired.html
@@ -0,0 +1,63 @@
+<html>
+ <head>
+ <title>
+ Test that ensures beforeunload is fired when session-history-in-parent is enabled
+ </title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ /*
+ * This test ensures beforeunload is fired on the current page
+ * when it is entering BFCache and the next page is coming out
+ * from BFCache
+ *
+ * (1) The controller page opens a new window, and page A is loaded there.
+ * (2) Page A then navigates to page B, and a beforeunload event
+ * listener is registered on page B.
+ * (3) Page B then navigates back to page A, and the beforeunload handler
+ * should send a message to the controller page.
+ * (4) Page A then navigates back to page B to check if page B has
+ * been successfully added to BFCache.
+ */
+
+ var bc = new BroadcastChannel("ship_beforeunload");
+ var pageshowCount = 0;
+ var beforeUnloadFired = false;
+ bc.onmessage = function(event) {
+ if (event.data.type == "pageshow") {
+ ++pageshowCount;
+ if (pageshowCount == 1) {
+ bc.postMessage({action: "navigate_to_page_b"});
+ } else if (pageshowCount == 2) {
+ ok(!event.data.persisted, "?pageb shouldn't in BFCache because it's the first navigation");
+ bc.postMessage({action: "register_beforeunload", loadNextPageFromSessionHistory: true});
+ } else if (pageshowCount == 3) {
+ ok(event.data.persisted, "navigated back to page A that was in BFCacache from page B");
+ ok(beforeUnloadFired, "beforeunload has fired on page B");
+ bc.postMessage({action: "back_to_page_b", forwardNavigateToPageB: true});
+ } else if (pageshowCount == 4) {
+ ok(event.data.persisted, "page B has beforeunload fired and also entered BFCache");
+ bc.postMessage({action: "close"});
+ SimpleTest.finish();
+ }
+ } else if (event.data == "beforeunload_fired") {
+ beforeUnloadFired = true;
+ }
+ }
+
+ function runTest() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["fission.bfcacheInParent", true],
+ ["docshell.shistory.bfcache.ship_allow_beforeunload_listeners", true]
+ ]},
+ function() {
+ window.open("file_ship_beforeunload_fired.html", "", "noopener");
+ }
+ );
+ }
+ </script>
+ <body onload="runTest()"></body>
+</html>
diff --git a/docshell/test/navigation/test_ship_beforeunload_fired_2.html b/docshell/test/navigation/test_ship_beforeunload_fired_2.html
new file mode 100644
index 0000000000..93669502a5
--- /dev/null
+++ b/docshell/test/navigation/test_ship_beforeunload_fired_2.html
@@ -0,0 +1,65 @@
+<html>
+ <head>
+ <title>
+ Test that ensures beforeunload is fired when session-history-in-parent is enabled
+ </title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ /*
+ * This test ensures beforeunload is fired on the current page
+ * when it is entering BFCache, and the next page is not coming from
+ * session history and also not coming out from BFCache.
+ *
+ * (1) The controller page opens a new window, and page A is loaded there.
+ * (2) Page A then navigates to page B, and a beforeunload event
+ * listener is registered on page B.
+ * (3) Page B then navigates to page C, and the beforeunload handler
+ * should send a message to the controller page.
+ * (4) Page C then navigates back to page B to check if page B has
+ * been successfully added to BFCache.
+ */
+
+ var bc = new BroadcastChannel("ship_beforeunload");
+ var pageshowCount = 0;
+
+ var beforeUnloadFired = false;
+ bc.onmessage = function(event) {
+ if (event.data.type == "pageshow") {
+ ++pageshowCount;
+ if (pageshowCount == 1) {
+ bc.postMessage({action: "navigate_to_page_b"});
+ } else if (pageshowCount == 2) {
+ ok(!event.data.persisted, "?page B shouldn't in BFCache because it's the first navigation");
+ bc.postMessage({action: "register_beforeunload",
+ loadNextPageFromSessionHistory: false});
+ } else if (pageshowCount == 3) {
+ ok(!event.data.persisted, "navigated to page C that was a new page");
+ ok(beforeUnloadFired, "beforeUnload should be fired on page B");
+ bc.postMessage({action: "back_to_page_b", forwardNavigateToPageB: false});
+ } else if (pageshowCount == 4) {
+ ok(event.data.persisted, "page B has been successfully added to BFCache");
+ bc.postMessage({action: "close"});
+ SimpleTest.finish();
+ }
+ } else if (event.data == "beforeunload_fired") {
+ beforeUnloadFired = true;
+ }
+ }
+
+ function runTest() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["fission.bfcacheInParent", true],
+ ["docshell.shistory.bfcache.ship_allow_beforeunload_listeners", true]
+ ]},
+ function() {
+ window.open("file_ship_beforeunload_fired.html", "", "noopener");
+ }
+ );
+ }
+ </script>
+ <body onload="runTest()"></body>
+</html>
diff --git a/docshell/test/navigation/test_ship_beforeunload_fired_3.html b/docshell/test/navigation/test_ship_beforeunload_fired_3.html
new file mode 100644
index 0000000000..8951f269c5
--- /dev/null
+++ b/docshell/test/navigation/test_ship_beforeunload_fired_3.html
@@ -0,0 +1,65 @@
+<html>
+ <head>
+ <title>
+ Test that ensures beforeunload is fired when session-history-in-parent is enabled
+ </title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ /*
+ * This test ensures beforeunload is fired on the current page
+ * when it is entering BFCache, and the next page is not coming out
+ * from BFCache, but coming from session history.
+ *
+ * (1) The controller page opens a new window, and page A is loaded there.
+ * (2) Page A then navigates to page B, and a beforeunload event
+ * listener is registered on page B.
+ * (3) Page B then navigates back to page A, and the beforeunload handler
+ * should send a message to the controller page.
+ * (4) Page A then navigates back to page B to check if page B has
+ * been successfully added to BFCache.
+ */
+
+ var bc = new BroadcastChannel("ship_beforeunload");
+ var pageshowCount = 0;
+
+ var beforeUnloadFired = false;
+ bc.onmessage = function(event) {
+ if (event.data.type == "pageshow") {
+ ++pageshowCount;
+ if (pageshowCount == 1) {
+ bc.postMessage({action: "navigate_to_page_b", blockBFCache: true});
+ } else if (pageshowCount == 2) {
+ ok(!event.data.persisted, "?page B shouldn't in BFCache because it's the first navigation");
+ bc.postMessage({action: "register_beforeunload",
+ loadNextPageFromSessionHistory: true});
+ } else if (pageshowCount == 3) {
+ ok(!event.data.persisted, "navigated back to page A that was session history but not in BFCache");
+ ok(beforeUnloadFired, "beforeUnload should be fired on page B");
+ bc.postMessage({action: "back_to_page_b", forwardNavigateToPageB: true});
+ } else if (pageshowCount == 4) {
+ ok(event.data.persisted, "page B has been successfully added to BFCache");
+ bc.postMessage({action: "close"});
+ SimpleTest.finish();
+ }
+ } else if (event.data == "beforeunload_fired") {
+ beforeUnloadFired = true;
+ }
+ }
+
+ function runTest() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["fission.bfcacheInParent", true],
+ ["docshell.shistory.bfcache.ship_allow_beforeunload_listeners", true]
+ ]},
+ function() {
+ window.open("file_ship_beforeunload_fired.html", "", "noopener");
+ }
+ );
+ }
+ </script>
+ <body onload="runTest()"></body>
+</html>
diff --git a/docshell/test/navigation/test_sibling-matching-parent.html b/docshell/test/navigation/test_sibling-matching-parent.html
new file mode 100644
index 0000000000..3c1bc768db
--- /dev/null
+++ b/docshell/test/navigation/test_sibling-matching-parent.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+window.onload = async function() {
+ document.getElementById("active").innerHTML =
+ '<iframe src="navigate.html#parent.frames[0],location"></iframe>' +
+ '<iframe src="navigate.html#child1,open"></iframe>' +
+ '<iframe src="navigate.html#child2,form"></iframe>' +
+ '<iframe src="navigate.html#child3,hyperlink"></iframe>';
+
+ await waitForFinishedFrames(4);
+
+ await isNavigated(frames[0], "Should be able to navigate sibling with on-domain parent by setting location.");
+ await isNavigated(frames[1], "Should be able to navigate sibling with on-domain parent by calling window.open.");
+ await isNavigated(frames[2], "Should be able to navigate sibling with on-domain parent by submitting form.");
+ await isNavigated(frames[3], "Should be able to navigate sibling with on-domain parent by targeted hyperlink.");
+
+ await cleanupWindows();
+ SimpleTest.finish();
+};
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
+<div id="frames">
+<iframe name="child0" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe name="child1" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe name="child2" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe name="child3" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+</div>
+<div id="active"></div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_sibling-off-domain.html b/docshell/test/navigation/test_sibling-off-domain.html
new file mode 100644
index 0000000000..cd70d1ae91
--- /dev/null
+++ b/docshell/test/navigation/test_sibling-off-domain.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+window.onload = async function() {
+ document.getElementById("active").innerHTML =
+ '<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#parent.frames[0],location"></iframe>' +
+ '<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#child1,open"></iframe>' +
+ '<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#child2,form"></iframe>' +
+ '<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#child3,hyperlink"></iframe>';
+
+ await waitForFinishedFrames(4);
+
+ isBlank(frames[0], "Should not be able to navigate off-domain sibling by setting location.");
+ isBlank(frames[1], "Should not be able to navigate off-domain sibling by calling window.open.");
+ isBlank(frames[2], "Should not be able to navigate off-domain sibling by submitting form.");
+ isBlank(frames[3], "Should not be able to navigate off-domain sibling by targeted hyperlink.");
+
+ await cleanupWindows();
+ SimpleTest.finish();
+};
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
+<div id="frames">
+<iframe name="child0" src="blank.html"></iframe>
+<iframe name="child1" src="blank.html"></iframe>
+<iframe name="child2" src="blank.html"></iframe>
+<iframe name="child3" src="blank.html"></iframe>
+</div>
+<div id="active"></div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_state_size.html b/docshell/test/navigation/test_state_size.html
new file mode 100644
index 0000000000..f089a460ec
--- /dev/null
+++ b/docshell/test/navigation/test_state_size.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test the max size of the data parameter of push/replaceState</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ function test() {
+ let tooLarge = SpecialPowers.getIntPref("browser.history.maxStateObjectSize");
+ let allowed = Math.floor(tooLarge / 2);
+
+ history.pushState(new Array(allowed).join("a"), "");
+ ok(true, "Adding a state should succeed.");
+
+ try {
+ history.pushState(new Array(tooLarge).join("a"), "");
+ ok(false, "Adding a too large state object should fail.");
+ } catch(ex) {
+ ok(true, "Adding a too large state object should fail.");
+ }
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_static_and_dynamic.html b/docshell/test/navigation/test_static_and_dynamic.html
new file mode 100644
index 0000000000..ff72a8188c
--- /dev/null
+++ b/docshell/test/navigation/test_static_and_dynamic.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for static and dynamic frames and forward-back </title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ var testCount = 0; // Used by the test files.
+
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_static_and_dynamic_1.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_triggeringprincipal_frame_nav.html b/docshell/test/navigation/test_triggeringprincipal_frame_nav.html
new file mode 100644
index 0000000000..d7046e9236
--- /dev/null
+++ b/docshell/test/navigation/test_triggeringprincipal_frame_nav.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1181370 - Test triggeringPrincipal for iframe navigations</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe1"></iframe>
+<iframe style="width:100%;" id="testframe2"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ *
+ * +------------------------------------+
+ * | +----------+ +--------------+ |
+ * | | Frame 1 | | Frame 2 | |
+ * | +----------+ | | |
+ * | | +----------+ | |
+ * | | | Subframe | | |
+ * | | +----------+ | |
+ * | +--------------+ |
+ * +------------------------------------+
+ *
+ * Frame1: test1.mochi.test
+ * Frame2: test2.mochi.test
+ * Subframe: test2.mochi.test
+ *
+ * (*) Frame1 and Subframe set their document.domain to mochi.test
+ * (*) Frame1 navigates the Subframe
+ * (*) TriggeringPrincipal for the Subframe navigation should be
+ * ==> test1.mochi.test
+ * (*) LoadingPrincipal for the Subframe navigation should be
+ * ==> test2.mochi.test
+ */
+
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+const BASEDOMAIN1 = "http://test1.mochi.test:8888/";
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+const BASEDOMAIN2 = "http://test2.mochi.test:8888/";
+const PATH = "tests/docshell/test/navigation/";
+const BASEURL1 = BASEDOMAIN1 + PATH;
+const BASEURL2 = BASEDOMAIN2 + PATH;
+const TRIGGERINGPRINCIPALURI = BASEURL1 + "file_triggeringprincipal_frame_1.html";
+const LOADINGPRINCIPALURI = BASEURL2 + "file_triggeringprincipal_frame_2.html";
+
+SimpleTest.waitForExplicitFinish();
+
+window.addEventListener("message", receiveMessage);
+
+function receiveMessage(event) {
+ is(event.data.triggeringPrincipalURI, TRIGGERINGPRINCIPALURI,
+ "TriggeringPrincipal should be the navigating iframe (Frame 1)");
+ is(event.data.loadingPrincipalURI, LOADINGPRINCIPALURI,
+ "LoadingPrincipal should be the enclosing iframe (Frame 2)");
+ is(event.data.referrerURI, BASEDOMAIN1,
+ "The path of Referrer should be trimmed (Frame 1)");
+
+ window.removeEventListener("message", receiveMessage);
+ SimpleTest.finish();
+}
+
+var frame1 = document.getElementById("testframe1");
+frame1.src = BASEURL1 + "file_triggeringprincipal_frame_1.html";
+
+var frame2 = document.getElementById("testframe2");
+frame2.src = BASEURL2 + "file_triggeringprincipal_frame_2.html";
+
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_triggeringprincipal_frame_same_origin_nav.html b/docshell/test/navigation/test_triggeringprincipal_frame_same_origin_nav.html
new file mode 100644
index 0000000000..4483efb13e
--- /dev/null
+++ b/docshell/test/navigation/test_triggeringprincipal_frame_same_origin_nav.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1639195 - Test triggeringPrincipal for iframe same-origin navigations</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe" src="http://example.com/"></iframe>
+
+<script type="text/javascript">
+/* We load an third-party iframe which then gets navigated by the iframe's
+ * parent by calling iframe.setAttribute("src", same-origin url) later in the
+ * test. We then verify the TriggeringPrincipal and LoadingPrincipal of the
+ * navigated iframe.
+ *
+ * +------------------------------------------+
+ * | |
+ * | +------------------+ |
+ * | | testframe | |
+ * | +------------------+ |
+ * | |
+ * | iframe.setAttribute("src", |
+ * | same-origin url) |
+ * | |
+ * +------------------------------------------+
+ */
+
+var testframe = document.getElementById("testframe");
+
+window.addEventListener("message", receiveMessage);
+
+const TRIGGERING_PRINCIPAL_URI =
+ "http://mochi.test:8888/tests/docshell/test/navigation/test_triggeringprincipal_frame_same_origin_nav.html";
+
+const LOADING_PRINCIPAL_URI = TRIGGERING_PRINCIPAL_URI;
+
+function receiveMessage(event) {
+ is(event.data.triggeringPrincipalURI.split("?")[0], TRIGGERING_PRINCIPAL_URI,
+ "TriggeringPrincipal should be the parent iframe");
+ is(event.data.loadingPrincipalURI.split("?")[0], TRIGGERING_PRINCIPAL_URI,
+ "LoadingPrincipal should be the parent iframe");
+
+ window.removeEventListener("message", receiveMessage);
+ SimpleTest.finish();
+}
+
+function performNavigation() {
+ testframe.removeEventListener("load", performNavigation);
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ testframe.setAttribute("src", "http://example.com/tests/docshell/test/navigation/file_triggeringprincipal_subframe_same_origin_nav.html");
+}
+
+// start the test
+SimpleTest.waitForExplicitFinish();
+
+testframe.addEventListener("load", performNavigation);
+</script>
+
+</body>
+</html>
diff --git a/docshell/test/navigation/test_triggeringprincipal_iframe_iframe_window_open.html b/docshell/test/navigation/test_triggeringprincipal_iframe_iframe_window_open.html
new file mode 100644
index 0000000000..115c5f4462
--- /dev/null
+++ b/docshell/test/navigation/test_triggeringprincipal_iframe_iframe_window_open.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+</head>
+<body>
+
+<iframe name="framea" id="framea" src="file_triggeringprincipal_iframe_iframe_window_open_frame_a.html"></iframe>
+<iframe name="frameb" id="frameb"></iframe>
+
+<script type="text/javascript">
+
+/* We load an iframe (Frame A) which then gets navigated by another iframe (Frame B)
+ * by calling window.open("http://", "Frame A") later in the test. We then verify the
+ * TriggeringPrincipal and LoadingPrincipal of the navigated iframe (Frame A).
+ *
+ * +---------------------------------------+
+ * | Parent |
+ * | |
+ * | +----------------------------+ |
+ * | | Frame A | |
+ * | | | |
+ * | | | |
+ * | +----------------------------+ |
+ * | |
+ * | +----------------------------+ |
+ * | | Frame B | |
+ * | | | |
+ * | | win.open("http://", "A") | |
+ * | +----------------------------+ |
+ * | |
+ * +---------------------------------------+
+ *
+ * Sequence of the test:
+ * [1] load Frame A
+ * [2] load Frame B which navigates A
+ * [3] load navigated Frame A and check triggeringPrincipal and loadingPrincipal
+ */
+
+const TRIGGERING_PRINCIPAL_URI =
+ "http://mochi.test:8888/tests/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_b.html";
+
+const LOADING_PRINCIPAL_URI =
+ "http://mochi.test:8888/tests/docshell/test/navigation/test_triggeringprincipal_iframe_iframe_window_open.html";
+
+var frameA = document.getElementById("framea");
+
+function checkResults() {
+ frameA.removeEventListener("load", checkResults);
+
+ var channel = SpecialPowers.wrap(frameA.contentWindow).docShell.currentDocumentChannel;
+ var triggeringPrincipal = channel.loadInfo.triggeringPrincipal.asciiSpec;
+ var loadingPrincipal = channel.loadInfo.loadingPrincipal.asciiSpec;
+
+ is(triggeringPrincipal, TRIGGERING_PRINCIPAL_URI,
+ "TriggeringPrincipal for targeted window.open() should be the iframe triggering the load");
+
+ is(frameA.contentDocument.referrer, TRIGGERING_PRINCIPAL_URI,
+ "Referrer for targeted window.open() should be the principal of the iframe triggering the load");
+
+ is(loadingPrincipal.split("?")[0], LOADING_PRINCIPAL_URI,
+ "LoadingPrincipal for targeted window.open() should be the containing document");
+
+ SimpleTest.finish();
+}
+
+function performNavigation() {
+ frameA.removeEventListener("load", performNavigation);
+ frameA.addEventListener("load", checkResults);
+
+ // load Frame B which then navigates Frame A
+ var frameB = document.getElementById("frameb");
+ frameB.src = "file_triggeringprincipal_iframe_iframe_window_open_frame_b.html";
+}
+
+// start the test
+SimpleTest.waitForExplicitFinish();
+
+frameA.addEventListener("load", performNavigation);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_triggeringprincipal_parent_iframe_window_open.html b/docshell/test/navigation/test_triggeringprincipal_parent_iframe_window_open.html
new file mode 100644
index 0000000000..1611ebf479
--- /dev/null
+++ b/docshell/test/navigation/test_triggeringprincipal_parent_iframe_window_open.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+</head>
+<body>
+
+<iframe name="testframe" id="testframe" src="file_triggeringprincipal_iframe_iframe_window_open_base.html"></iframe>
+
+<script type="text/javascript">
+
+/* We load an iframe which then gets navigated by the iframe's parent by calling
+ * window.open("http://", iframe) later in the test. We then verify the
+ * TriggeringPrincipal and LoadingPrincipal of the navigated iframe.
+ *
+ * +------------------------------------------+
+ * | |
+ * | +------------------+ |
+ * | | testframe | |
+ * | +------------------+ |
+ * | |
+ * | window.open("http://", "testframe"); |
+ * | |
+ * +------------------------------------------+
+ */
+
+const TRIGGERING_PRINCIPAL_URI =
+ "http://mochi.test:8888/tests/docshell/test/navigation/test_triggeringprincipal_parent_iframe_window_open.html";
+
+const LOADING_PRINCIPAL_URI = TRIGGERING_PRINCIPAL_URI;
+
+var testframe = document.getElementById("testframe");
+
+function checkResults() {
+ testframe.removeEventListener("load", checkResults);
+
+ var channel = SpecialPowers.wrap(testframe.contentWindow).docShell.currentDocumentChannel;
+ var triggeringPrincipal = channel.loadInfo.triggeringPrincipal.asciiSpec.split("?")[0];
+ var loadingPrincipal = channel.loadInfo.loadingPrincipal.asciiSpec.split("?")[0];
+
+ is(triggeringPrincipal, TRIGGERING_PRINCIPAL_URI,
+ "TriggeringPrincipal for targeted window.open() should be the principal of the document");
+
+ is(testframe.contentDocument.referrer.split("?")[0], TRIGGERING_PRINCIPAL_URI,
+ "Referrer for targeted window.open() should be the principal of the document");
+
+ is(loadingPrincipal, LOADING_PRINCIPAL_URI,
+ "LoadingPrincipal for targeted window.open() should be the <iframe>.ownerDocument");
+
+ SimpleTest.finish();
+}
+
+function performNavigation() {
+ testframe.removeEventListener("load", performNavigation);
+ testframe.addEventListener("load", checkResults);
+ window.open("file_triggeringprincipal_parent_iframe_window_open_nav.html", "testframe");
+}
+
+// start the test
+SimpleTest.waitForExplicitFinish();
+
+testframe.addEventListener("load", performNavigation);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_triggeringprincipal_window_open.html b/docshell/test/navigation/test_triggeringprincipal_window_open.html
new file mode 100644
index 0000000000..439a125f97
--- /dev/null
+++ b/docshell/test/navigation/test_triggeringprincipal_window_open.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+</head>
+<body>
+
+<script type="text/javascript">
+
+/* We call window.open() using different URIs and make sure the triggeringPrincipal
+ * loadingPrincipal are correct.
+ * Test1: window.open(http:)
+ * Test2: window.open(javascript:)
+ */
+
+const TRIGGERING_PRINCIPAL_URI =
+ "http://mochi.test:8888/tests/docshell/test/navigation/test_triggeringprincipal_window_open.html";
+
+SimpleTest.waitForExplicitFinish();
+
+const NUM_TESTS = 2;
+var test_counter = 0;
+
+function checkFinish() {
+ test_counter++;
+ if (test_counter === NUM_TESTS) {
+ SimpleTest.finish();
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Test 1: window.open(http:)
+var httpWin = window.open("file_triggeringprincipal_window_open.html", "_blank", "width=10,height=10");
+httpWin.onload = function() {
+ var httpChannel = SpecialPowers.wrap(httpWin).docShell.currentDocumentChannel;
+ var httpTriggeringPrincipal = httpChannel.loadInfo.triggeringPrincipal.asciiSpec;
+ var httpLoadingPrincipal = httpChannel.loadInfo.loadingPrincipal;
+
+ is(httpTriggeringPrincipal.split("?")[0], TRIGGERING_PRINCIPAL_URI,
+ "TriggeringPrincipal for window.open(http:) should be the principal of the document");
+
+ is(httpWin.document.referrer.split("?")[0], TRIGGERING_PRINCIPAL_URI,
+ "Referrer for window.open(http:) should be the principal of the document");
+
+ is(httpLoadingPrincipal, null,
+ "LoadingPrincipal for window.open(http:) should be null");
+
+ httpWin.close();
+ checkFinish();
+};
+
+// ----------------------------------------------------------------------------
+// Test 2: window.open(javascript:)
+var jsWin = window.open("javascript:'<html><body>js</body></html>';", "_blank", "width=10,height=10");
+jsWin.onload = function() {
+ var jsChannel = SpecialPowers.wrap(jsWin).docShell.currentDocumentChannel;
+ var jsTriggeringPrincipal = jsChannel.loadInfo.triggeringPrincipal.asciiSpec;
+ var jsLoadingPrincipal = jsChannel.loadInfo.loadingPrincipal;
+
+ is(jsTriggeringPrincipal.split("?")[0], TRIGGERING_PRINCIPAL_URI,
+ "TriggeringPrincipal for window.open(javascript:) should be the principal of the document");
+
+ is(jsWin.document.referrer, "",
+ "Referrer for window.open(javascript:) should be empty");
+
+ is(jsLoadingPrincipal, null,
+ "LoadingPrincipal for window.open(javascript:) should be null");
+
+ jsWin.close();
+ checkFinish();
+};
+
+</script>
+</pre>
+</body>
+</html>