diff options
Diffstat (limited to 'docshell/test')
682 files changed, 34856 insertions, 0 deletions
diff --git a/docshell/test/browser/Bug1622420Child.sys.mjs b/docshell/test/browser/Bug1622420Child.sys.mjs new file mode 100644 index 0000000000..c5520d5943 --- /dev/null +++ b/docshell/test/browser/Bug1622420Child.sys.mjs @@ -0,0 +1,9 @@ +export class Bug1622420Child extends JSWindowActorChild { + receiveMessage(msg) { + switch (msg.name) { + case "hasWindowContextForTopBC": + return !!this.browsingContext.top.currentWindowContext; + } + return null; + } +} diff --git a/docshell/test/browser/Bug422543Child.sys.mjs b/docshell/test/browser/Bug422543Child.sys.mjs new file mode 100644 index 0000000000..524ac33ffd --- /dev/null +++ b/docshell/test/browser/Bug422543Child.sys.mjs @@ -0,0 +1,98 @@ +class SHistoryListener { + constructor() { + this.retval = true; + this.last = "initial"; + } + + OnHistoryNewEntry(aNewURI) { + this.last = "newentry"; + } + + OnHistoryGotoIndex() { + this.last = "gotoindex"; + } + + OnHistoryPurge() { + this.last = "purge"; + } + + OnHistoryReload() { + this.last = "reload"; + return this.retval; + } + + OnHistoryReplaceEntry() {} +} +SHistoryListener.prototype.QueryInterface = ChromeUtils.generateQI([ + "nsISHistoryListener", + "nsISupportsWeakReference", +]); + +let listeners; + +export class Bug422543Child extends JSWindowActorChild { + constructor() { + super(); + } + + actorCreated() { + if (listeners) { + return; + } + + this.shistory = this.docShell.nsIWebNavigation.sessionHistory; + listeners = [new SHistoryListener(), new SHistoryListener()]; + + for (let listener of listeners) { + this.shistory.legacySHistory.addSHistoryListener(listener); + } + } + + cleanup() { + for (let listener of listeners) { + this.shistory.legacySHistory.removeSHistoryListener(listener); + } + this.shistory = null; + listeners = null; + return {}; + } + + getListenerStatus() { + return listeners.map(l => l.last); + } + + resetListeners() { + for (let listener of listeners) { + listener.last = "initial"; + } + + return {}; + } + + notifyReload() { + let history = this.shistory.legacySHistory; + let rval = history.notifyOnHistoryReload(); + return { rval }; + } + + setRetval({ num, val }) { + listeners[num].retval = val; + return {}; + } + + receiveMessage(msg) { + switch (msg.name) { + case "cleanup": + return this.cleanup(); + case "getListenerStatus": + return this.getListenerStatus(); + case "notifyReload": + return this.notifyReload(); + case "resetListeners": + return this.resetListeners(); + case "setRetval": + return this.setRetval(msg.data); + } + return null; + } +} diff --git a/docshell/test/browser/browser.ini b/docshell/test/browser/browser.ini new file mode 100644 index 0000000000..c8ed2e345b --- /dev/null +++ b/docshell/test/browser/browser.ini @@ -0,0 +1,244 @@ +[DEFAULT] +support-files = + Bug422543Child.sys.mjs + dummy_page.html + favicon_bug655270.ico + file_bug234628-1-child.html + file_bug234628-1.html + file_bug234628-10-child.xhtml + file_bug234628-10.html + file_bug234628-11-child.xhtml + file_bug234628-11-child.xhtml^headers^ + file_bug234628-11.html + file_bug234628-2-child.html + file_bug234628-2.html + file_bug234628-3-child.html + file_bug234628-3.html + file_bug234628-4-child.html + file_bug234628-4.html + file_bug234628-5-child.html + file_bug234628-5.html + file_bug234628-6-child.html + file_bug234628-6-child.html^headers^ + file_bug234628-6.html + file_bug234628-8-child.html + file_bug234628-8.html + file_bug234628-9-child.html + file_bug234628-9.html + file_bug420605.html + file_bug503832.html + file_bug655270.html + file_bug670318.html + file_bug673087-1.html + file_bug673087-1.html^headers^ + file_bug673087-1-child.html + file_bug673087-2.html + file_bug852909.pdf + file_bug852909.png + file_bug1046022.html + file_bug1206879.html + file_bug1328501.html + file_bug1328501_frame.html + file_bug1328501_framescript.js + file_bug1543077-3-child.html + file_bug1543077-3.html + file_multiple_pushState.html + file_onbeforeunload_0.html + file_onbeforeunload_1.html + file_onbeforeunload_2.html + file_onbeforeunload_3.html + print_postdata.sjs + test-form_sjis.html + timelineMarkers-04.html + browser_timelineMarkers-frame-02.js + browser_timelineMarkers-frame-03.js + browser_timelineMarkers-frame-04.js + browser_timelineMarkers-frame-05.js + head.js + frame-head.js + file_data_load_inherit_csp.html + file_click_link_within_view_source.html + onload_message.html + onpageshow_message.html + file_cross_process_csp_inheritance.html + file_open_about_blank.html + file_slow_load.sjs + file_bug1648464-1.html + file_bug1648464-1-child.html + file_bug1688368-1.sjs + file_bug1691153.html + file_bug1716290-1.sjs + file_bug1716290-2.sjs + file_bug1716290-3.sjs + file_bug1716290-4.sjs + file_bug1736248-1.html + +[browser_alternate_fixup_middle_click_link.js] +https_first_disabled = true +[browser_backforward_userinteraction.js] +support-files = + dummy_iframe_page.html +skip-if = + os == "linux" && bits == 64 && !debug # Bug 1607713 +[browser_backforward_userinteraction_about.js] +[browser_backforward_userinteraction_systemprincipal.js] +skip-if = + os == "win" && os_version == "6.1" # Skip on Azure - frequent failure +[browser_bug1543077-3.js] +[browser_bug1594938.js] +[browser_bug1206879.js] +https_first_disabled = true +[browser_bug1309900_crossProcessHistoryNavigation.js] +https_first_disabled = true +[browser_bug1328501.js] +https_first_disabled = true +[browser_bug1347823.js] +[browser_bug134911.js] +[browser_bug1415918_beforeunload_options.js] +https_first_disabled = true +[browser_bug1622420.js] +support-files = + file_bug1622420.html + Bug1622420Child.sys.mjs +[browser_bug1673702.js] +https_first_disabled = true +skip-if = + os == "linux" && bits == 64 && os_version == "18.04" && debug # Bug 1674513 + os == "win" # Bug 1674513 +support-files = + file_bug1673702.json + file_bug1673702.json^headers^ +[browser_bug1674464.js] +https_first_disabled = true +skip-if = !fission || !crashreporter # On a crash we only keep history when fission is enabled. +[browser_bug1719178.js] +[browser_bug1757005.js] +[browser_bug1769189.js] +[browser_bug1798780.js] +[browser_bug234628-1.js] +[browser_bug234628-10.js] +[browser_bug234628-11.js] +[browser_bug234628-2.js] +[browser_bug234628-3.js] +[browser_bug234628-4.js] +[browser_bug234628-5.js] +[browser_bug234628-6.js] +[browser_bug234628-8.js] +[browser_bug234628-9.js] +[browser_bug349769.js] +[browser_bug388121-1.js] +[browser_bug388121-2.js] +[browser_bug420605.js] +skip-if = verify +[browser_bug422543.js] +https_first_disabled = true +[browser_bug441169.js] +[browser_bug503832.js] +skip-if = verify +[browser_bug554155.js] +[browser_bug655270.js] +[browser_bug655273.js] +[browser_bug670318.js] +[browser_bug673087-1.js] +[browser_bug673087-2.js] +[browser_bug673467.js] +[browser_bug852909.js] +skip-if = (verify && debug && (os == 'win')) +[browser_bug92473.js] +[browser_csp_sandbox_no_script_js_uri.js] +support-files = + file_csp_sandbox_no_script_js_uri.html + file_csp_sandbox_no_script_js_uri.html^headers^ +[browser_data_load_inherit_csp.js] +[browser_dataURI_unique_opaque_origin.js] +[browser_fission_maxOrigins.js] +https_first_disabled = true +[browser_frameloader_swap_with_bfcache.js] +[browser_backforward_restore_scroll.js] +https_first_disabled = true +support-files = + file_backforward_restore_scroll.html + file_backforward_restore_scroll.html^headers^ +[browser_targetTopLevelLinkClicksToBlank.js] +[browser_title_in_session_history.js] +skip-if = !sessionHistoryInParent +[browser_uriFixupIntegration.js] +[browser_uriFixupAlternateRedirects.js] +https_first_disabled = true +support-files = + redirect_to_example.sjs +[browser_loadURI_postdata.js] +[browser_multiple_pushState.js] +https_first_disabled = true +[browser_onbeforeunload_frame.js] +support-files = head_browser_onbeforeunload.js +[browser_onbeforeunload_parent.js] +support-files = head_browser_onbeforeunload.js +[browser_onbeforeunload_navigation.js] +skip-if = (os == 'win' && !debug) # bug 1300351 +[browser_onunload_stop.js] +https_first_disabled = true +[browser_overlink.js] +support-files = + overlink_test.html +[browser_platform_emulation.js] +[browser_search_notification.js] +[browser_tab_touch_events.js] +[browser_timelineMarkers-01.js] +[browser_timelineMarkers-02.js] +skip-if = true # Bug 1220415 +[browser_timelineMarkers-03.js] +[browser_timelineMarkers-04.js] +[browser_timelineMarkers-05.js] +[browser_ua_emulation.js] +[browser_history_triggeringprincipal_viewsource.js] +https_first_disabled = true +[browser_click_link_within_view_source.js] +[browser_browsingContext-01.js] +https_first_disabled = true +[browser_browsingContext-02.js] +https_first_disabled = true +[browser_browsingContext-getAllBrowsingContextsInSubtree.js] +[browser_browsingContext-getWindowByName.js] +[browser_browsingContext-embedder.js] +[browser_browsingContext-webProgress.js] +skip-if = + os == "linux" && bits == 64 && !debug # Bug 1721261 + os == "win" && os_version == "6.1" # Skip on Azure - frequent failure +https_first_disabled = true +[browser_csp_uir.js] +support-files = + file_csp_uir.html + file_csp_uir_dummy.html +[browser_cross_process_csp_inheritance.js] +https_first_disabled = true +[browser_tab_replace_while_loading.js] +skip-if = (os == 'linux' && bits == 64 && os_version == '18.04') || (os == "win") # Bug 1604237, Bug 1671794 +[browser_browsing_context_attached.js] +https_first_disabled = true +[browser_browsing_context_discarded.js] +https_first_disabled = true +[browser_fall_back_to_https.js] +https_first_disabled = true +skip-if = (os == 'mac') +[browser_badCertDomainFixup.js] +[browser_viewsource_chrome_to_content.js] +[browser_viewsource_multipart.js] +support-files = + file_basic_multipart.sjs +[browser_bug1648464-1.js] +[browser_bug1688368-1.js] +[browser_bug1691153.js] +https_first_disabled = true +[browser_bug1705872.js] +[browser_isInitialDocument.js] +https_first_disabled = true +[browser_bug1716290-1.js] +[browser_bug1716290-2.js] +[browser_bug1716290-3.js] +[browser_bug1716290-4.js] +[browser_bfcache_copycommand.js] +skip-if = + os == "linux" && bits == 64 # Bug 1730593 + os == "win" && os_version == "6.1" # Skip on Azure - frequent failure +[browser_bug1736248-1.js] diff --git a/docshell/test/browser/browser_alternate_fixup_middle_click_link.js b/docshell/test/browser/browser_alternate_fixup_middle_click_link.js new file mode 100644 index 0000000000..8a55e12e52 --- /dev/null +++ b/docshell/test/browser/browser_alternate_fixup_middle_click_link.js @@ -0,0 +1,59 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Check that we don't do alternate fixup when users middle-click + * broken links in the content document. + */ +add_task(async function test_alt_fixup_middle_click() { + await BrowserTestUtils.withNewTab("about:blank", async browser => { + await SpecialPowers.spawn(browser, [], () => { + let link = content.document.createElement("a"); + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + link.href = "http://example/foo"; + link.textContent = "Me, me, click me!"; + content.document.body.append(link); + }); + let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser); + await BrowserTestUtils.synthesizeMouseAtCenter( + "a[href]", + { button: 1 }, + browser + ); + let tab = await newTabPromise; + let { browsingContext } = tab.linkedBrowser; + // Account for the possibility of a race, where the error page has already loaded: + if ( + !browsingContext.currentWindowGlobal?.documentURI.spec.startsWith( + "about:neterror" + ) + ) { + await BrowserTestUtils.browserLoaded( + tab.linkedBrowser, + false, + null, + true + ); + } + // TBH, if the test fails, we probably force-crash because we try to reach + // *www.* example.com, which isn't proxied by the test infrastructure so + // will forcibly abort the test. But we need some asserts so they might as + // well be meaningful: + is( + tab.linkedBrowser.currentURI.spec, + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example/foo", + "URL for tab should be correct." + ); + + ok( + browsingContext.currentWindowGlobal.documentURI.spec.startsWith( + "about:neterror" + ), + "Should be showing error page." + ); + BrowserTestUtils.removeTab(tab); + }); +}); diff --git a/docshell/test/browser/browser_backforward_restore_scroll.js b/docshell/test/browser/browser_backforward_restore_scroll.js new file mode 100644 index 0000000000..d8c570b1a9 --- /dev/null +++ b/docshell/test/browser/browser_backforward_restore_scroll.js @@ -0,0 +1,54 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const ROOT = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://mochi.test:8888" +); +const URL1 = ROOT + "file_backforward_restore_scroll.html"; +// eslint-disable-next-line @microsoft/sdl/no-insecure-url +const URL2 = "http://example.net/"; + +const SCROLL0 = 500; +const SCROLL1 = 1000; + +function promiseBrowserLoaded(url) { + return BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, url); +} + +add_task(async function test() { + await BrowserTestUtils.openNewForegroundTab(gBrowser, URL1); + + // Scroll the 2 frames. + let children = gBrowser.selectedBrowser.browsingContext.children; + await SpecialPowers.spawn(children[0], [SCROLL0], scrollY => + content.scrollTo(0, scrollY) + ); + await SpecialPowers.spawn(children[1], [SCROLL1], scrollY => + content.scrollTo(0, scrollY) + ); + + // Navigate forwards then backwards. + let loaded = promiseBrowserLoaded(URL2); + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, URL2); + await loaded; + + loaded = promiseBrowserLoaded(URL1); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + content.history.back(); + }); + await loaded; + + // And check the results. + children = gBrowser.selectedBrowser.browsingContext.children; + await SpecialPowers.spawn(children[0], [SCROLL0], scrollY => { + Assert.equal(content.scrollY, scrollY, "frame 0 has correct scroll"); + }); + await SpecialPowers.spawn(children[1], [SCROLL1], scrollY => { + Assert.equal(content.scrollY, scrollY, "frame 1 has correct scroll"); + }); + + gBrowser.removeTab(gBrowser.selectedTab); +}); diff --git a/docshell/test/browser/browser_backforward_userinteraction.js b/docshell/test/browser/browser_backforward_userinteraction.js new file mode 100644 index 0000000000..77e0df402d --- /dev/null +++ b/docshell/test/browser/browser_backforward_userinteraction.js @@ -0,0 +1,374 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_PAGE = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" + ) + "dummy_page.html"; +const IFRAME_PAGE = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" + ) + "dummy_iframe_page.html"; + +async function assertMenulist(entries, baseURL = TEST_PAGE) { + // Wait for the session data to be flushed before continuing the test + await new Promise(resolve => + SessionStore.getSessionHistory(gBrowser.selectedTab, resolve) + ); + + let backButton = document.getElementById("back-button"); + let contextMenu = document.getElementById("backForwardMenu"); + + info("waiting for the history menu to open"); + + let popupShownPromise = BrowserTestUtils.waitForEvent( + contextMenu, + "popupshown" + ); + EventUtils.synthesizeMouseAtCenter(backButton, { + type: "contextmenu", + button: 2, + }); + await popupShownPromise; + + ok(true, "history menu opened"); + + let nodes = contextMenu.childNodes; + + is( + nodes.length, + entries.length, + "Has the expected number of contextMenu entries" + ); + + for (let i = 0; i < entries.length; i++) { + let node = nodes[i]; + is( + node.getAttribute("uri").replace(/[?|#]/, "!"), + baseURL + "!entry=" + entries[i], + "contextMenu node has the correct uri" + ); + } + + let popupHiddenPromise = BrowserTestUtils.waitForEvent( + contextMenu, + "popuphidden" + ); + contextMenu.hidePopup(); + await popupHiddenPromise; +} + +// There are different ways of loading a page, but they should exhibit roughly the same +// back-forward behavior for the purpose of requiring user interaction. Thus, we +// have a utility function that runs the same test with a parameterized method of loading +// new URLs. +async function runTopLevelTest(loadMethod, useHashes = false) { + let p = useHashes ? "#" : "?"; + + // Test with both pref on and off + for (let requireUserInteraction of [true, false]) { + Services.prefs.setBoolPref( + "browser.navigation.requireUserInteraction", + requireUserInteraction + ); + + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_PAGE + p + "entry=0" + ); + let browser = tab.linkedBrowser; + + assertBackForwardState(false, false); + + await loadMethod(TEST_PAGE + p + "entry=1"); + + assertBackForwardState(true, false); + await assertMenulist([1, 0]); + + await loadMethod(TEST_PAGE + p + "entry=2"); + + assertBackForwardState(true, false); + await assertMenulist(requireUserInteraction ? [2, 0] : [2, 1, 0]); + + await loadMethod(TEST_PAGE + p + "entry=3"); + + info("Adding user interaction for entry=3"); + // Add some user interaction to entry 3 + await BrowserTestUtils.synthesizeMouse( + "body", + 0, + 0, + {}, + browser.browsingContext, + true + ); + + assertBackForwardState(true, false); + await assertMenulist(requireUserInteraction ? [3, 0] : [3, 2, 1, 0]); + + await loadMethod(TEST_PAGE + p + "entry=4"); + + assertBackForwardState(true, false); + await assertMenulist(requireUserInteraction ? [4, 3, 0] : [4, 3, 2, 1, 0]); + + info("Adding user interaction for entry=4"); + // Add some user interaction to entry 4 + await BrowserTestUtils.synthesizeMouse( + "body", + 0, + 0, + {}, + browser.browsingContext, + true + ); + + await loadMethod(TEST_PAGE + p + "entry=5"); + + assertBackForwardState(true, false); + await assertMenulist( + requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0] + ); + + await goBack(TEST_PAGE + p + "entry=4"); + await goBack(TEST_PAGE + p + "entry=3"); + + if (!requireUserInteraction) { + await goBack(TEST_PAGE + p + "entry=2"); + await goBack(TEST_PAGE + p + "entry=1"); + } + + assertBackForwardState(true, true); + await assertMenulist( + requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0] + ); + + await goBack(TEST_PAGE + p + "entry=0"); + + assertBackForwardState(false, true); + + if (!requireUserInteraction) { + await goForward(TEST_PAGE + p + "entry=1"); + await goForward(TEST_PAGE + p + "entry=2"); + } + + await goForward(TEST_PAGE + p + "entry=3"); + + assertBackForwardState(true, true); + await assertMenulist( + requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0] + ); + + await goForward(TEST_PAGE + p + "entry=4"); + + assertBackForwardState(true, true); + await assertMenulist( + requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0] + ); + + await goForward(TEST_PAGE + p + "entry=5"); + + assertBackForwardState(true, false); + await assertMenulist( + requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0] + ); + + BrowserTestUtils.removeTab(tab); + } + + Services.prefs.clearUserPref("browser.navigation.requireUserInteraction"); +} + +async function runIframeTest(loadMethod) { + // Test with both pref on and off + for (let requireUserInteraction of [true, false]) { + Services.prefs.setBoolPref( + "browser.navigation.requireUserInteraction", + requireUserInteraction + ); + + // First test the boring case where we only have one iframe. + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + IFRAME_PAGE + "?entry=0" + ); + let browser = tab.linkedBrowser; + + assertBackForwardState(false, false); + + await loadMethod(TEST_PAGE + "?sub_entry=1", "frame1"); + + assertBackForwardState(true, false); + await assertMenulist([0, 0], IFRAME_PAGE); + + await loadMethod(TEST_PAGE + "?sub_entry=2", "frame1"); + + assertBackForwardState(true, false); + await assertMenulist( + requireUserInteraction ? [0, 0] : [0, 0, 0], + IFRAME_PAGE + ); + + let bc = await SpecialPowers.spawn(browser, [], function() { + return content.document.getElementById("frame1").browsingContext; + }); + + // Add some user interaction to sub entry 2 + await BrowserTestUtils.synthesizeMouse("body", 0, 0, {}, bc, true); + + await loadMethod(TEST_PAGE + "?sub_entry=3", "frame1"); + + assertBackForwardState(true, false); + await assertMenulist( + requireUserInteraction ? [0, 0, 0] : [0, 0, 0, 0], + IFRAME_PAGE + ); + + await loadMethod(TEST_PAGE + "?sub_entry=4", "frame1"); + + assertBackForwardState(true, false); + await assertMenulist( + requireUserInteraction ? [0, 0, 0] : [0, 0, 0, 0, 0], + IFRAME_PAGE + ); + + if (!requireUserInteraction) { + await goBack(TEST_PAGE + "?sub_entry=3", true); + } + + await goBack(TEST_PAGE + "?sub_entry=2", true); + + assertBackForwardState(true, true); + await assertMenulist( + requireUserInteraction ? [0, 0, 0] : [0, 0, 0, 0, 0], + IFRAME_PAGE + ); + + await loadMethod(IFRAME_PAGE + "?entry=1"); + + assertBackForwardState(true, false); + await assertMenulist( + requireUserInteraction ? [1, 0, 0] : [1, 0, 0, 0], + IFRAME_PAGE + ); + + BrowserTestUtils.removeTab(tab); + + // Two iframes, now we're talking. + tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + IFRAME_PAGE + "?entry=0" + ); + browser = tab.linkedBrowser; + + await loadMethod(IFRAME_PAGE + "?entry=1"); + + assertBackForwardState(true, false); + await assertMenulist(requireUserInteraction ? [1, 0] : [1, 0], IFRAME_PAGE); + + // Add some user interaction to frame 1. + bc = await SpecialPowers.spawn(browser, [], function() { + return content.document.getElementById("frame1").browsingContext; + }); + await BrowserTestUtils.synthesizeMouse("body", 0, 0, {}, bc, true); + + // Add some user interaction to frame 2. + bc = await SpecialPowers.spawn(browser, [], function() { + return content.document.getElementById("frame2").browsingContext; + }); + await BrowserTestUtils.synthesizeMouse("body", 0, 0, {}, bc, true); + + // Navigate frame 2. + await loadMethod(TEST_PAGE + "?sub_entry=1", "frame2"); + + assertBackForwardState(true, false); + await assertMenulist( + requireUserInteraction ? [1, 1, 0] : [1, 1, 0], + IFRAME_PAGE + ); + + // Add some user interaction to frame 1, again. + bc = await SpecialPowers.spawn(browser, [], function() { + return content.document.getElementById("frame1").browsingContext; + }); + await BrowserTestUtils.synthesizeMouse("body", 0, 0, {}, bc, true); + + // Navigate frame 2, again. + await loadMethod(TEST_PAGE + "?sub_entry=2", "frame2"); + + assertBackForwardState(true, false); + await assertMenulist( + requireUserInteraction ? [1, 1, 1, 0] : [1, 1, 1, 0], + IFRAME_PAGE + ); + + await goBack(TEST_PAGE + "?sub_entry=1", true); + + assertBackForwardState(true, true); + await assertMenulist( + requireUserInteraction ? [1, 1, 1, 0] : [1, 1, 1, 0], + IFRAME_PAGE + ); + + await goBack(TEST_PAGE + "?sub_entry=0", true); + + assertBackForwardState(true, true); + await assertMenulist( + requireUserInteraction ? [1, 1, 1, 0] : [1, 1, 1, 0], + IFRAME_PAGE + ); + + BrowserTestUtils.removeTab(tab); + } + + Services.prefs.clearUserPref("browser.navigation.requireUserInteraction"); +} + +// Test that when the pref is flipped, we are skipping history +// entries without user interaction when following links with hash URIs. +add_task(async function test_hashURI() { + async function followLinkHash(url) { + info(`Creating and following a link to ${url}`); + let browser = gBrowser.selectedBrowser; + let loaded = BrowserTestUtils.waitForLocationChange(gBrowser, url); + await SpecialPowers.spawn(browser, [url], function(url) { + let a = content.document.createElement("a"); + a.href = url; + content.document.body.appendChild(a); + a.click(); + }); + await loaded; + info(`Loaded ${url}`); + } + + await runTopLevelTest(followLinkHash, true); +}); + +// Test that when the pref is flipped, we are skipping history +// entries without user interaction when using history.pushState. +add_task(async function test_pushState() { + await runTopLevelTest(pushState); +}); + +// Test that when the pref is flipped, we are skipping history +// entries without user interaction when following a link. +add_task(async function test_followLink() { + await runTopLevelTest(followLink); +}); + +// Test that when the pref is flipped, we are skipping history +// entries without user interaction when navigating inside an iframe +// using history.pushState. +add_task(async function test_iframe_pushState() { + await runIframeTest(pushState); +}); + +// Test that when the pref is flipped, we are skipping history +// entries without user interaction when navigating inside an iframe +// by following links. +add_task(async function test_iframe_followLink() { + await runIframeTest(followLink); +}); diff --git a/docshell/test/browser/browser_backforward_userinteraction_about.js b/docshell/test/browser/browser_backforward_userinteraction_about.js new file mode 100644 index 0000000000..606fcc45c5 --- /dev/null +++ b/docshell/test/browser/browser_backforward_userinteraction_about.js @@ -0,0 +1,67 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_PAGE = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" + ) + "dummy_page.html"; + +// Regression test for navigating back after visiting an about: page +// loaded in the parent process. +add_task(async function test_about_back() { + // Test with both pref on and off + for (let requireUserInteraction of [true, false]) { + Services.prefs.setBoolPref( + "browser.navigation.requireUserInteraction", + requireUserInteraction + ); + + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_PAGE + "?entry=0" + ); + let browser = tab.linkedBrowser; + assertBackForwardState(false, false); + + await followLink(TEST_PAGE + "?entry=1"); + assertBackForwardState(true, false); + + await followLink(TEST_PAGE + "?entry=2"); + assertBackForwardState(true, false); + + // Add some user interaction to entry 2 + await BrowserTestUtils.synthesizeMouse("body", 0, 0, {}, browser, true); + + await loadURI("about:config"); + assertBackForwardState(true, false); + + await goBack(TEST_PAGE + "?entry=2"); + assertBackForwardState(true, true); + + if (!requireUserInteraction) { + await goBack(TEST_PAGE + "?entry=1"); + assertBackForwardState(true, true); + } + + await goBack(TEST_PAGE + "?entry=0"); + assertBackForwardState(false, true); + + if (!requireUserInteraction) { + await goForward(TEST_PAGE + "?entry=1"); + assertBackForwardState(true, true); + } + + await goForward(TEST_PAGE + "?entry=2"); + assertBackForwardState(true, true); + + await goForward("about:config"); + assertBackForwardState(true, false); + + BrowserTestUtils.removeTab(tab); + } + + Services.prefs.clearUserPref("browser.navigation.requireUserInteraction"); +}); diff --git a/docshell/test/browser/browser_backforward_userinteraction_systemprincipal.js b/docshell/test/browser/browser_backforward_userinteraction_systemprincipal.js new file mode 100644 index 0000000000..f46048632e --- /dev/null +++ b/docshell/test/browser/browser_backforward_userinteraction_systemprincipal.js @@ -0,0 +1,112 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_PAGE = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" + ) + "dummy_page.html"; + +async function runTest(privilegedLoad) { + let prefVals; + // Test with both pref on and off, unless parent-controlled pref is enabled. + // This distinction can be removed once SHIP is enabled by default. + if ( + Services.prefs.getBoolPref("browser.tabs.documentchannel.parent-controlled") + ) { + prefVals = [false]; + } else { + prefVals = [true, false]; + } + + for (let requireUserInteraction of prefVals) { + Services.prefs.setBoolPref( + "browser.navigation.requireUserInteraction", + requireUserInteraction + ); + + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_PAGE + "?entry=0" + ); + + assertBackForwardState(false, false); + + await followLink(TEST_PAGE + "?entry=1"); + + assertBackForwardState(true, false); + + await followLink(TEST_PAGE + "?entry=2"); + + assertBackForwardState(true, false); + + await followLink(TEST_PAGE + "?entry=3"); + + assertBackForwardState(true, false); + + // Entry 4 will be added through a user action in browser chrome, + // giving user interaction to entry 3. Entry 4 should not gain automatic + // user interaction. + await privilegedLoad(TEST_PAGE + "?entry=4"); + + assertBackForwardState(true, false); + + await followLink(TEST_PAGE + "?entry=5"); + + assertBackForwardState(true, false); + + if (!requireUserInteraction) { + await goBack(TEST_PAGE + "?entry=4"); + } + await goBack(TEST_PAGE + "?entry=3"); + + if (!requireUserInteraction) { + await goBack(TEST_PAGE + "?entry=2"); + await goBack(TEST_PAGE + "?entry=1"); + } + + assertBackForwardState(true, true); + + await goBack(TEST_PAGE + "?entry=0"); + + assertBackForwardState(false, true); + + if (!requireUserInteraction) { + await goForward(TEST_PAGE + "?entry=1"); + await goForward(TEST_PAGE + "?entry=2"); + } + + await goForward(TEST_PAGE + "?entry=3"); + + assertBackForwardState(true, true); + + if (!requireUserInteraction) { + await goForward(TEST_PAGE + "?entry=4"); + } + + await goForward(TEST_PAGE + "?entry=5"); + + assertBackForwardState(true, false); + + BrowserTestUtils.removeTab(tab); + } + + Services.prefs.clearUserPref("browser.navigation.requireUserInteraction"); +} + +// Test that we add a user interaction flag to the previous site when loading +// a new site from user interaction with privileged UI, e.g. through the +// URL bar. +add_task(async function test_urlBar() { + await runTest(async function(url) { + info(`Loading ${url} via the URL bar.`); + let browser = gBrowser.selectedBrowser; + let loaded = BrowserTestUtils.browserLoaded(browser, false, url); + gURLBar.focus(); + gURLBar.value = url; + gURLBar.goButton.click(); + await loaded; + }); +}); diff --git a/docshell/test/browser/browser_badCertDomainFixup.js b/docshell/test/browser/browser_badCertDomainFixup.js new file mode 100644 index 0000000000..783360d7b7 --- /dev/null +++ b/docshell/test/browser/browser_badCertDomainFixup.js @@ -0,0 +1,92 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// This test checks if we are correctly fixing https URLs by prefixing +// with www. when we encounter a SSL_ERROR_BAD_CERT_DOMAIN error. +// For example, https://example.com -> https://www.example.com. + +const PREF_BAD_CERT_DOMAIN_FIX_ENABLED = + "security.bad_cert_domain_error.url_fix_enabled"; +const PREF_ALLOW_HIJACKING_LOCALHOST = + "network.proxy.allow_hijacking_localhost"; + +const BAD_CERT_DOMAIN_ERROR_URL = "https://badcertdomain.example.com:443"; +const FIXED_URL = "https://www.badcertdomain.example.com/"; + +const BAD_CERT_DOMAIN_ERROR_URL2 = + "https://mismatch.badcertdomain.example.com:443"; +const IPV4_ADDRESS = "https://127.0.0.3:433"; +const BAD_CERT_DOMAIN_ERROR_PORT = "https://badcertdomain.example.com:82"; + +async function verifyErrorPage(errorPageURL) { + let certErrorLoaded = BrowserTestUtils.waitForErrorPage( + gBrowser.selectedBrowser + ); + BrowserTestUtils.loadURI(gBrowser, errorPageURL); + await certErrorLoaded; + + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function() { + let ec; + await ContentTaskUtils.waitForCondition(() => { + ec = content.document.getElementById("errorCode"); + return ec.textContent; + }, "Error code has been set inside the advanced button panel"); + is( + ec.textContent, + "SSL_ERROR_BAD_CERT_DOMAIN", + "Correct error code is shown" + ); + }); +} + +// Test that "www." is prefixed to a https url when we encounter a bad cert domain +// error if the "www." form is included in the certificate's subjectAltNames. +add_task(async function prefixBadCertDomain() { + // Turn off the pref and ensure that we show the error page as expected. + Services.prefs.setBoolPref(PREF_BAD_CERT_DOMAIN_FIX_ENABLED, false); + + gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); + await verifyErrorPage(BAD_CERT_DOMAIN_ERROR_URL); + info("Cert error is shown as expected when the fixup pref is disabled"); + + // Turn on the pref and test that we fix the HTTPS URL. + Services.prefs.setBoolPref(PREF_BAD_CERT_DOMAIN_FIX_ENABLED, true); + gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); + let loadSuccessful = BrowserTestUtils.browserLoaded( + gBrowser.selectedBrowser, + false, + FIXED_URL + ); + BrowserTestUtils.loadURI(gBrowser, BAD_CERT_DOMAIN_ERROR_URL); + await loadSuccessful; + + info("The URL was fixed as expected"); + + BrowserTestUtils.removeTab(gBrowser.selectedTab); + BrowserTestUtils.removeTab(gBrowser.selectedTab); +}); + +// Test that we don't prefix "www." to a https url when we encounter a bad cert domain +// error under certain conditions. +add_task(async function ignoreBadCertDomain() { + Services.prefs.setBoolPref(PREF_BAD_CERT_DOMAIN_FIX_ENABLED, true); + gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); + + // Test for when "www." form is not present in the certificate. + await verifyErrorPage(BAD_CERT_DOMAIN_ERROR_URL2); + info("Certificate error was shown as expected"); + + // Test that urls with IP addresses are not fixed. + Services.prefs.setBoolPref(PREF_ALLOW_HIJACKING_LOCALHOST, true); + await verifyErrorPage(IPV4_ADDRESS); + Services.prefs.clearUserPref(PREF_ALLOW_HIJACKING_LOCALHOST); + info("Certificate error was shown as expected for an IP address"); + + // Test that urls with ports are not fixed. + await verifyErrorPage(BAD_CERT_DOMAIN_ERROR_PORT); + info("Certificate error was shown as expected for a host with port"); + + BrowserTestUtils.removeTab(gBrowser.selectedTab); +}); diff --git a/docshell/test/browser/browser_bfcache_copycommand.js b/docshell/test/browser/browser_bfcache_copycommand.js new file mode 100644 index 0000000000..6b9a5870aa --- /dev/null +++ b/docshell/test/browser/browser_bfcache_copycommand.js @@ -0,0 +1,98 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +function dummyPageURL(domain, query = "") { + return ( + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + `https://${domain}` + ) + + "dummy_page.html" + + query + ); +} + +async function openContextMenu(browser) { + let contextMenu = document.getElementById("contentAreaContextMenu"); + let awaitPopupShown = BrowserTestUtils.waitForEvent( + contextMenu, + "popupshown" + ); + await BrowserTestUtils.synthesizeMouse( + "body", + 1, + 1, + { + type: "contextmenu", + button: 2, + }, + browser + ); + await awaitPopupShown; +} + +async function closeContextMenu() { + let contextMenu = document.getElementById("contentAreaContextMenu"); + let awaitPopupHidden = BrowserTestUtils.waitForEvent( + contextMenu, + "popuphidden" + ); + contextMenu.hidePopup(); + await awaitPopupHidden; +} + +async function testWithDomain(domain) { + // Passing a query to make sure the next load is never a same-document + // navigation. + let dummy = dummyPageURL("example.org", "?start"); + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, dummy); + let browser = tab.linkedBrowser; + + let sel = await SpecialPowers.spawn(browser, [], function() { + let sel = content.getSelection(); + sel.removeAllRanges(); + sel.selectAllChildren(content.document.body); + return sel.toString(); + }); + + await openContextMenu(browser); + + let copyItem = document.getElementById("context-copy"); + ok(!copyItem.disabled, "Copy item should be enabled if text is selected."); + + await closeContextMenu(); + + let loaded = BrowserTestUtils.browserLoaded(browser, false, null, true); + BrowserTestUtils.loadURI(browser, dummyPageURL(domain)); + await loaded; + loaded = BrowserTestUtils.waitForLocationChange(gBrowser, dummy); + browser.goBack(); + await loaded; + + let sel2 = await SpecialPowers.spawn(browser, [], function() { + return content.getSelection().toString(); + }); + is(sel, sel2, "Selection should remain when coming out of BFCache."); + + await openContextMenu(browser); + + ok(!copyItem.disabled, "Copy item should be enabled if text is selected."); + + await closeContextMenu(); + + await BrowserTestUtils.removeTab(tab); +} + +add_task(async function testValidSameOrigin() { + await testWithDomain("example.org"); +}); + +add_task(async function testValidCrossOrigin() { + await testWithDomain("example.com"); +}); + +add_task(async function testInvalid() { + await testWithDomain("this.is.invalid"); +}); diff --git a/docshell/test/browser/browser_browsingContext-01.js b/docshell/test/browser/browser_browsingContext-01.js new file mode 100644 index 0000000000..81a32985a0 --- /dev/null +++ b/docshell/test/browser/browser_browsingContext-01.js @@ -0,0 +1,178 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const URL = "about:blank"; + +async function getBrowsingContextId(browser, id) { + return SpecialPowers.spawn(browser, [id], async function(id) { + let contextId = content.window.docShell.browsingContext.id; + + let frames = [content.window]; + while (frames.length) { + let frame = frames.pop(); + let target = frame.document.getElementById(id); + if (target) { + contextId = target.docShell.browsingContext.id; + break; + } + + frames = frames.concat(Array.from(frame.frames)); + } + + return contextId; + }); +} + +async function addFrame(browser, id, parentId) { + return SpecialPowers.spawn(browser, [{ parentId, id }], async function({ + parentId, + id, + }) { + let parent = null; + if (parentId) { + let frames = [content.window]; + while (frames.length) { + let frame = frames.pop(); + let target = frame.document.getElementById(parentId); + if (target) { + parent = target.contentWindow.document.body; + break; + } + frames = frames.concat(Array.from(frame.frames)); + } + } else { + parent = content.document.body; + } + + let frame = await new Promise(resolve => { + let frame = content.document.createElement("iframe"); + frame.id = id || ""; + frame.url = "about:blank"; + frame.onload = () => resolve(frame); + parent.appendChild(frame); + }); + + return frame.contentWindow.docShell.browsingContext.id; + }); +} + +async function removeFrame(browser, id) { + return SpecialPowers.spawn(browser, [id], async function(id) { + let frames = [content.window]; + while (frames.length) { + let frame = frames.pop(); + let target = frame.document.getElementById(id); + if (target) { + target.remove(); + break; + } + + frames = frames.concat(Array.from(frame.frames)); + } + }); +} + +function getBrowsingContextById(id) { + return BrowsingContext.get(id); +} + +add_task(async function() { + await BrowserTestUtils.withNewTab({ gBrowser, url: URL }, async function( + browser + ) { + let topId = await getBrowsingContextId(browser, ""); + let topContext = getBrowsingContextById(topId); + isnot(topContext, null); + is(topContext.parent, null); + is( + topId, + browser.browsingContext.id, + "<browser> has the correct browsingContext" + ); + is( + browser.browserId, + topContext.browserId, + "browsing context should have a correct <browser> id" + ); + + let id0 = await addFrame(browser, "frame0"); + let browsingContext0 = getBrowsingContextById(id0); + isnot(browsingContext0, null); + is(browsingContext0.parent, topContext); + + await removeFrame(browser, "frame0"); + + is(topContext.children.indexOf(browsingContext0), -1); + + // TODO(farre): Handle browsingContext removal [see Bug 1486719]. + todo_isnot(browsingContext0.parent, topContext); + }); +}); + +add_task(async function() { + // If Fission is disabled, the pref is no-op. + await SpecialPowers.pushPrefEnv({ set: [["fission.bfcacheInParent", true]] }); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com" + ) + "dummy_page.html", + }, + async function(browser) { + let path = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com" + ); + await SpecialPowers.spawn(browser, [path], async function(path) { + var bc = new content.BroadcastChannel("browser_browsingContext"); + function waitForMessage(command) { + let p = new Promise(resolve => { + bc.addEventListener("message", e => resolve(e), { once: true }); + }); + command(); + return p; + } + + // Open a new window and wait for the message. + let e1 = await waitForMessage(_ => + content.window.open(path + "onpageshow_message.html", "", "noopener") + ); + + is(e1.data, "pageshow", "Got page show"); + + let e2 = await waitForMessage(_ => bc.postMessage("createiframe")); + is(e2.data.framesLength, 1, "Here we should have an iframe"); + + let e3 = await waitForMessage(_ => bc.postMessage("nextpage")); + + is(e3.data.event, "load"); + is(e3.data.framesLength, 0, "Here there shouldn't be an iframe"); + + // Return to the previous document. N.B. we expect to trigger + // BFCache here, hence we wait for pageshow. + let e4 = await waitForMessage(_ => bc.postMessage("back")); + + is(e4.data, "pageshow"); + + let e5 = await waitForMessage(_ => bc.postMessage("queryframes")); + is(e5.data.framesLength, 1, "And again there should be an iframe"); + + is(e5.outerWindowId, e2.outerWindowId, "BF cache cached outer window"); + is(e5.browsingContextId, e2.browsingContextId, "BF cache cached BC"); + + let e6 = await waitForMessage(_ => bc.postMessage("close")); + is(e6.data, "closed"); + + bc.close(); + }); + } + ); +}); diff --git a/docshell/test/browser/browser_browsingContext-02.js b/docshell/test/browser/browser_browsingContext-02.js new file mode 100644 index 0000000000..5d09802412 --- /dev/null +++ b/docshell/test/browser/browser_browsingContext-02.js @@ -0,0 +1,233 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function() { + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function(browser) { + const BASE1 = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com" + ); + const BASE2 = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://test1.example.com" + ); + const URL = BASE1 + "onload_message.html"; + let sixth = BrowserTestUtils.waitForNewTab( + gBrowser, + URL + "#sixth", + true, + true + ); + let seventh = BrowserTestUtils.waitForNewTab( + gBrowser, + URL + "#seventh", + true, + true + ); + let browserIds = await SpecialPowers.spawn( + browser, + [{ base1: BASE1, base2: BASE2 }], + async function({ base1, base2 }) { + let top = content; + top.name = "top"; + top.location.href += "#top"; + + let contexts = { + top: top.location.href, + first: base1 + "dummy_page.html#first", + third: base2 + "dummy_page.html#third", + second: base1 + "dummy_page.html#second", + fourth: base2 + "dummy_page.html#fourth", + fifth: base1 + "dummy_page.html#fifth", + sixth: base1 + "onload_message.html#sixth", + seventh: base1 + "onload_message.html#seventh", + }; + + function addFrame(target, name) { + return content.SpecialPowers.spawn( + target, + [name, contexts[name]], + async (name, context) => { + let doc = this.content.document; + + let frame = doc.createElement("iframe"); + doc.body.appendChild(frame); + frame.name = name; + frame.src = context; + await new Promise(resolve => { + frame.addEventListener("load", resolve, { once: true }); + }); + return frame.browsingContext; + } + ); + } + + function addWindow(target, name, { options, resolve }) { + return content.SpecialPowers.spawn( + target, + [name, contexts[name], options, resolve], + (name, context, options, resolve) => { + let win = this.content.open(context, name, options); + let bc = win && win.docShell.browsingContext; + + if (resolve) { + return new Promise(resolve => + this.content.addEventListener("message", () => resolve(bc)) + ); + } + return Promise.resolve({ name }); + } + ); + } + + // We're going to create a tree that looks like the + // following. + // + // top sixth seventh + // / \ + // / \ / + // first second + // / \ / + // / \ + // third fourth - - - + // / + // / + // fifth + // + // The idea is to have one top level non-auxiliary browsing + // context, five nested, one top level auxiliary with an + // opener, and one top level without an opener. Given that + // set of related and one unrelated browsing contexts we + // wish to confirm that targeting is able to find + // appropriate browsing contexts. + + // BrowsingContext.findWithName requires access checks, which + // can only be performed in the process of the accessor BC's + // docShell. + function findWithName(bc, name) { + return content.SpecialPowers.spawn(bc, [bc, name], (bc, name) => { + return bc.findWithName(name); + }); + } + + async function reachable(start, target) { + info(start.name, target.name); + is( + await findWithName(start, target.name), + target, + [start.name, "can reach", target.name].join(" ") + ); + } + + async function unreachable(start, target) { + is( + await findWithName(start, target.name), + null, + [start.name, "can't reach", target.name].join(" ") + ); + } + + let first = await addFrame(top, "first"); + info("first"); + let second = await addFrame(top, "second"); + info("second"); + let third = await addFrame(first, "third"); + info("third"); + let fourth = await addFrame(first, "fourth"); + info("fourth"); + let fifth = await addFrame(fourth, "fifth"); + info("fifth"); + let sixth = await addWindow(fourth, "sixth", { resolve: true }); + info("sixth"); + let seventh = await addWindow(fourth, "seventh", { + options: ["noopener"], + }); + info("seventh"); + + let origin1 = [first, second, fifth, sixth]; + let origin2 = [third, fourth]; + + let topBC = BrowsingContext.getFromWindow(top); + let frames = new Map([ + [topBC, [topBC, first, second, third, fourth, fifth, sixth]], + [first, [topBC, ...origin1, third, fourth]], + [second, [topBC, ...origin1, third, fourth]], + [third, [topBC, ...origin2, fifth, sixth]], + [fourth, [topBC, ...origin2, fifth, sixth]], + [fifth, [topBC, ...origin1, third, fourth]], + [sixth, [...origin1, third, fourth]], + ]); + + for (let [start, accessible] of frames) { + for (let frame of frames.keys()) { + if (accessible.includes(frame)) { + await reachable(start, frame); + } else { + await unreachable(start, frame); + } + } + await unreachable(start, seventh); + } + + let topBrowserId = topBC.browserId; + ok(topBrowserId > 0, "Should have a browser ID."); + for (let [name, bc] of Object.entries({ + first, + second, + third, + fourth, + fifth, + })) { + is( + bc.browserId, + topBrowserId, + `${name} frame should have the same browserId as top.` + ); + } + + ok(sixth.browserId > 0, "sixth should have a browserId."); + isnot( + sixth.browserId, + topBrowserId, + "sixth frame should have a different browserId to top." + ); + + return [topBrowserId, sixth.browserId]; + } + ); + + [sixth, seventh] = await Promise.all([sixth, seventh]); + + is( + browser.browserId, + browserIds[0], + "browser should have the right browserId." + ); + is( + browser.browsingContext.browserId, + browserIds[0], + "browser's BrowsingContext should have the right browserId." + ); + is( + sixth.linkedBrowser.browserId, + browserIds[1], + "sixth should have the right browserId." + ); + is( + sixth.linkedBrowser.browsingContext.browserId, + browserIds[1], + "sixth's BrowsingContext should have the right browserId." + ); + + for (let tab of [sixth, seventh]) { + BrowserTestUtils.removeTab(tab); + } + } + ); +}); diff --git a/docshell/test/browser/browser_browsingContext-embedder.js b/docshell/test/browser/browser_browsingContext-embedder.js new file mode 100644 index 0000000000..9473a46eb4 --- /dev/null +++ b/docshell/test/browser/browser_browsingContext-embedder.js @@ -0,0 +1,156 @@ +"use strict"; + +function observeOnce(topic) { + return new Promise(resolve => { + Services.obs.addObserver(function observer(aSubject, aTopic) { + if (topic == aTopic) { + Services.obs.removeObserver(observer, topic); + setTimeout(() => resolve(aSubject), 0); + } + }, topic); + }); +} + +add_task(async function runTest() { + let fissionWindow = await BrowserTestUtils.openNewBrowserWindow({ + fission: true, + remote: true, + }); + + info(`chrome, parent`); + let chromeBC = fissionWindow.docShell.browsingContext; + ok(chromeBC.currentWindowGlobal, "Should have a current WindowGlobal"); + is(chromeBC.embedderWindowGlobal, null, "chrome has no embedder global"); + is(chromeBC.embedderElement, null, "chrome has no embedder element"); + is(chromeBC.parent, null, "chrome has no parent"); + + // Open a new tab, and check that basic frames work out. + let tab = await BrowserTestUtils.openNewForegroundTab({ + gBrowser: fissionWindow.gBrowser, + }); + + info(`root, parent`); + let rootBC = tab.linkedBrowser.browsingContext; + ok(rootBC.currentWindowGlobal, "[parent] root has a window global"); + is( + rootBC.embedderWindowGlobal, + chromeBC.currentWindowGlobal, + "[parent] root has chrome as embedder global" + ); + is( + rootBC.embedderElement, + tab.linkedBrowser, + "[parent] root has browser as embedder element" + ); + is(rootBC.parent, null, "[parent] root has no parent"); + + // Test with an in-process frame + let frameId = await SpecialPowers.spawn(tab.linkedBrowser, [], async () => { + info(`root, child`); + let rootBC = content.docShell.browsingContext; + is(rootBC.embedderElement, null, "[child] root has no embedder"); + is(rootBC.parent, null, "[child] root has no parent"); + + info(`frame, child`); + let iframe = content.document.createElement("iframe"); + content.document.body.appendChild(iframe); + + let frameBC = iframe.contentWindow.docShell.browsingContext; + is(frameBC.embedderElement, iframe, "[child] frame embedded within iframe"); + is(frameBC.parent, rootBC, "[child] frame has root as parent"); + + return frameBC.id; + }); + + info(`frame, parent`); + let frameBC = BrowsingContext.get(frameId); + ok(frameBC.currentWindowGlobal, "[parent] frame has a window global"); + is( + frameBC.embedderWindowGlobal, + rootBC.currentWindowGlobal, + "[parent] frame has root as embedder global" + ); + is(frameBC.embedderElement, null, "[parent] frame has no embedder element"); + is(frameBC.parent, rootBC, "[parent] frame has root as parent"); + + // Test with an out-of-process iframe. + + let oopID = await SpecialPowers.spawn(tab.linkedBrowser, [], async () => { + info(`creating oop iframe`); + let oop = content.document.createElement("iframe"); + oop.setAttribute("src", "https://example.com"); + content.document.body.appendChild(oop); + + await new Promise(resolve => { + oop.addEventListener("load", resolve, { once: true }); + }); + + info(`oop frame, child`); + let oopBC = oop.frameLoader.browsingContext; + is(oopBC.embedderElement, oop, "[child] oop frame embedded within iframe"); + is( + oopBC.parent, + content.docShell.browsingContext, + "[child] frame has root as parent" + ); + + return oopBC.id; + }); + + info(`oop frame, parent`); + let oopBC = BrowsingContext.get(oopID); + is( + oopBC.embedderWindowGlobal, + rootBC.currentWindowGlobal, + "[parent] oop frame has root as embedder global" + ); + is(oopBC.embedderElement, null, "[parent] oop frame has no embedder element"); + is(oopBC.parent, rootBC, "[parent] oop frame has root as parent"); + + info(`waiting for oop window global`); + ok(oopBC.currentWindowGlobal, "[parent] oop frame has a window global"); + + // Open a new window, and adopt |tab| into it. + + let newWindow = await BrowserTestUtils.openNewBrowserWindow({ + fission: true, + remote: true, + }); + + info(`new chrome, parent`); + let newChromeBC = newWindow.docShell.browsingContext; + ok(newChromeBC.currentWindowGlobal, "Should have a current WindowGlobal"); + is( + newChromeBC.embedderWindowGlobal, + null, + "new chrome has no embedder global" + ); + is(newChromeBC.embedderElement, null, "new chrome has no embedder element"); + is(newChromeBC.parent, null, "new chrome has no parent"); + + isnot(newChromeBC, chromeBC, "different chrome browsing context"); + + info(`adopting tab`); + let newTab = newWindow.gBrowser.adoptTab(tab); + + is( + newTab.linkedBrowser.browsingContext, + rootBC, + "[parent] root browsing context survived" + ); + is( + rootBC.embedderWindowGlobal, + newChromeBC.currentWindowGlobal, + "[parent] embedder window global updated" + ); + is( + rootBC.embedderElement, + newTab.linkedBrowser, + "[parent] embedder element updated" + ); + is(rootBC.parent, null, "[parent] root has no parent"); + + info(`closing window`); + await BrowserTestUtils.closeWindow(newWindow); + await BrowserTestUtils.closeWindow(fissionWindow); +}); diff --git a/docshell/test/browser/browser_browsingContext-getAllBrowsingContextsInSubtree.js b/docshell/test/browser/browser_browsingContext-getAllBrowsingContextsInSubtree.js new file mode 100644 index 0000000000..1aba3c00aa --- /dev/null +++ b/docshell/test/browser/browser_browsingContext-getAllBrowsingContextsInSubtree.js @@ -0,0 +1,53 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +async function addFrame(url) { + let iframe = content.document.createElement("iframe"); + await new Promise(resolve => { + iframe.addEventListener("load", resolve, { once: true }); + iframe.src = url; + content.document.body.appendChild(iframe); + }); + return iframe.browsingContext; +} + +add_task(async function() { + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async browser => { + // Add 15 example.com frames to the toplevel document. + let frames = await Promise.all( + Array.from({ length: 15 }).map(_ => + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + SpecialPowers.spawn(browser, ["http://example.com/"], addFrame) + ) + ); + + // Add an example.org subframe to each example.com frame. + let subframes = await Promise.all( + Array.from({ length: 15 }).map((_, i) => + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + SpecialPowers.spawn(frames[i], ["http://example.org/"], addFrame) + ) + ); + + Assert.deepEqual( + subframes[0].getAllBrowsingContextsInSubtree(), + [subframes[0]], + "Childless context only has self in subtree" + ); + Assert.deepEqual( + frames[0].getAllBrowsingContextsInSubtree(), + [frames[0], subframes[0]], + "Single-child context has 2 contexts in subtree" + ); + Assert.deepEqual( + browser.browsingContext.getAllBrowsingContextsInSubtree(), + [browser.browsingContext, ...frames, ...subframes], + "Toplevel context has all subtree contexts" + ); + } + ); +}); diff --git a/docshell/test/browser/browser_browsingContext-getWindowByName.js b/docshell/test/browser/browser_browsingContext-getWindowByName.js new file mode 100644 index 0000000000..a51951dd2e --- /dev/null +++ b/docshell/test/browser/browser_browsingContext-getWindowByName.js @@ -0,0 +1,34 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +function addWindow(name) { + var blank = Cc["@mozilla.org/supports-string;1"].createInstance( + Ci.nsISupportsString + ); + blank.data = "about:blank"; + let promise = BrowserTestUtils.waitForNewWindow({ + anyWindow: true, + url: "about:blank", + }); + Services.ww.openWindow( + null, + AppConstants.BROWSER_CHROME_URL, + name, + "chrome,dialog=no", + blank + ); + return promise; +} + +add_task(async function() { + let windows = [await addWindow("first"), await addWindow("second")]; + + for (let w of windows) { + isnot(w, null); + is(Services.ww.getWindowByName(w.name, null), w, `Found ${w.name}`); + } + + await Promise.all(windows.map(BrowserTestUtils.closeWindow)); +}); diff --git a/docshell/test/browser/browser_browsingContext-webProgress.js b/docshell/test/browser/browser_browsingContext-webProgress.js new file mode 100644 index 0000000000..53d3e3a085 --- /dev/null +++ b/docshell/test/browser/browser_browsingContext-webProgress.js @@ -0,0 +1,238 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function() { + const tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:blank" + ); + const browser = tab.linkedBrowser; + const aboutBlankBrowsingContext = browser.browsingContext; + const { webProgress } = aboutBlankBrowsingContext; + ok( + webProgress, + "Got a WebProgress interface on BrowsingContext in the parent process" + ); + is( + webProgress.browsingContext, + browser.browsingContext, + "WebProgress.browsingContext refers to the right BrowsingContext" + ); + + const onLocationChanged = waitForNextLocationChange(webProgress); + const newLocation = "data:text/html;charset=utf-8,first-page"; + let loaded = BrowserTestUtils.browserLoaded(browser); + BrowserTestUtils.loadURI(browser, newLocation); + await loaded; + + const firstPageBrowsingContext = browser.browsingContext; + const isFissionAndBfcacheInParentEnabled = + SpecialPowers.useRemoteSubframes && + SpecialPowers.Services.prefs.getBoolPref("fission.bfcacheInParent"); + if (isFissionAndBfcacheInParentEnabled) { + isnot( + aboutBlankBrowsingContext, + firstPageBrowsingContext, + "With fission and bfcache in parent, navigations spawn a new BrowsingContext" + ); + } else { + is( + aboutBlankBrowsingContext, + firstPageBrowsingContext, + "Without fission or bfcache in parent, navigations reuse the same BrowsingContext" + ); + } + + info("Wait for onLocationChange to be fired"); + { + const { + browsingContext, + location, + request, + flags, + } = await onLocationChanged; + is( + browsingContext, + firstPageBrowsingContext, + "Location change fires on the new BrowsingContext" + ); + ok(location instanceof Ci.nsIURI); + is(location.spec, newLocation); + ok(request instanceof Ci.nsIChannel); + is(request.URI.spec, newLocation); + is(flags, 0); + } + + const onIframeLocationChanged = waitForNextLocationChange(webProgress); + const iframeLocation = "data:text/html;charset=utf-8,iframe"; + const iframeBC = await SpecialPowers.spawn( + browser, + [iframeLocation], + async url => { + const iframe = content.document.createElement("iframe"); + await new Promise(resolve => { + iframe.addEventListener("load", resolve, { once: true }); + iframe.src = url; + content.document.body.appendChild(iframe); + }); + + return iframe.browsingContext; + } + ); + ok( + iframeBC.webProgress, + "The iframe BrowsingContext also exposes a WebProgress" + ); + { + const { + browsingContext, + location, + request, + flags, + } = await onIframeLocationChanged; + is( + browsingContext, + iframeBC, + "Iframe location change fires on the iframe BrowsingContext" + ); + ok(location instanceof Ci.nsIURI); + is(location.spec, iframeLocation); + ok(request instanceof Ci.nsIChannel); + is(request.URI.spec, iframeLocation); + is(flags, 0); + } + + const onSecondLocationChanged = waitForNextLocationChange(webProgress); + const onSecondPageDocumentStart = waitForNextDocumentStart(webProgress); + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + const secondLocation = "http://example.com/document-builder.sjs?html=com"; + loaded = BrowserTestUtils.browserLoaded(browser); + BrowserTestUtils.loadURI(browser, secondLocation); + await loaded; + + const secondPageBrowsingContext = browser.browsingContext; + if (isFissionAndBfcacheInParentEnabled) { + isnot( + firstPageBrowsingContext, + secondPageBrowsingContext, + "With fission and bfcache in parent, navigations spawn a new BrowsingContext" + ); + } else { + is( + firstPageBrowsingContext, + secondPageBrowsingContext, + "Without fission or bfcache in parent, navigations reuse the same BrowsingContext" + ); + } + { + const { + browsingContext, + location, + request, + flags, + } = await onSecondLocationChanged; + is( + browsingContext, + secondPageBrowsingContext, + "Second location change fires on the new BrowsingContext" + ); + ok(location instanceof Ci.nsIURI); + is(location.spec, secondLocation); + ok(request instanceof Ci.nsIChannel); + is(request.URI.spec, secondLocation); + is(flags, 0); + } + { + const { browsingContext, request } = await onSecondPageDocumentStart; + is( + browsingContext, + firstPageBrowsingContext, + "STATE_START, when navigating to another process, fires on the BrowsingContext we navigate *from*" + ); + ok(request instanceof Ci.nsIChannel); + is(request.URI.spec, secondLocation); + } + + const onBackLocationChanged = waitForNextLocationChange(webProgress, true); + const onBackDocumentStart = waitForNextDocumentStart(webProgress); + browser.goBack(); + + { + const { + browsingContext, + location, + request, + flags, + } = await onBackLocationChanged; + is( + browsingContext, + firstPageBrowsingContext, + "location change, when navigating back, fires on the BrowsingContext we navigate *to*" + ); + ok(location instanceof Ci.nsIURI); + is(location.spec, newLocation); + ok(request instanceof Ci.nsIChannel); + is(request.URI.spec, newLocation); + is(flags, 0); + } + { + const { browsingContext, request } = await onBackDocumentStart; + is( + browsingContext, + secondPageBrowsingContext, + "STATE_START, when navigating back, fires on the BrowsingContext we navigate *from*" + ); + ok(request instanceof Ci.nsIChannel); + is(request.URI.spec, newLocation); + } + + BrowserTestUtils.removeTab(tab); +}); + +function waitForNextLocationChange(webProgress, onlyTopLevel = false) { + return new Promise(resolve => { + const wpl = { + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), + onLocationChange(progress, request, location, flags) { + if (onlyTopLevel && progress.browsingContext.parent) { + // Ignore non-toplevel. + return; + } + webProgress.removeProgressListener(wpl, Ci.nsIWebProgress.NOTIFY_ALL); + resolve({ + browsingContext: progress.browsingContext, + location, + request, + flags, + }); + }, + }; + webProgress.addProgressListener(wpl, Ci.nsIWebProgress.NOTIFY_ALL); + }); +} + +function waitForNextDocumentStart(webProgress) { + return new Promise(resolve => { + const wpl = { + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), + onStateChange(progress, request, flags, status) { + if ( + flags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT && + flags & Ci.nsIWebProgressListener.STATE_START + ) { + webProgress.removeProgressListener(wpl, Ci.nsIWebProgress.NOTIFY_ALL); + resolve({ browsingContext: progress.browsingContext, request }); + } + }, + }; + webProgress.addProgressListener(wpl, Ci.nsIWebProgress.NOTIFY_ALL); + }); +} diff --git a/docshell/test/browser/browser_browsing_context_attached.js b/docshell/test/browser/browser_browsing_context_attached.js new file mode 100644 index 0000000000..60ef5e4aa6 --- /dev/null +++ b/docshell/test/browser/browser_browsing_context_attached.js @@ -0,0 +1,179 @@ +"use strict"; + +const TEST_PATH = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com" + ) + "dummy_page.html"; + +const TOPIC = "browsing-context-attached"; + +async function observeAttached(callback) { + let attached = new Map(); + + function observer(subject, topic, data) { + is(topic, TOPIC, "observing correct topic"); + ok(BrowsingContext.isInstance(subject), "subject to be a BrowsingContext"); + is(typeof data, "string", "data to be a String"); + info(`*** bc id=${subject.id}, why=${data}`); + attached.set(subject.id, { browsingContext: subject, why: data }); + } + + Services.obs.addObserver(observer, TOPIC); + try { + await callback(); + return attached; + } finally { + Services.obs.removeObserver(observer, TOPIC); + } +} + +add_task(async function toplevelForNewWindow() { + let win; + + let attached = await observeAttached(async () => { + win = await BrowserTestUtils.openNewBrowserWindow(); + }); + + ok( + attached.has(win.browsingContext.id), + "got notification for window's chrome browsing context" + ); + is( + attached.get(win.browsingContext.id).why, + "attach", + "expected reason for chrome browsing context" + ); + + ok( + attached.has(win.gBrowser.selectedBrowser.browsingContext.id), + "got notification for toplevel browsing context" + ); + is( + attached.get(win.gBrowser.selectedBrowser.browsingContext.id).why, + "attach", + "expected reason for toplevel browsing context" + ); + + await BrowserTestUtils.closeWindow(win); +}); + +add_task(async function toplevelForNewTab() { + let tab; + + let attached = await observeAttached(async () => { + tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); + }); + + ok( + !attached.has(window.browsingContext.id), + "no notification for the current window's chrome browsing context" + ); + ok( + attached.has(tab.linkedBrowser.browsingContext.id), + "got notification for toplevel browsing context" + ); + is( + attached.get(tab.linkedBrowser.browsingContext.id).why, + "attach", + "expected reason for toplevel browsing context" + ); + + BrowserTestUtils.removeTab(tab); +}); + +add_task(async function subframe() { + let browsingContext; + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); + + let attached = await observeAttached(async () => { + browsingContext = await SpecialPowers.spawn(tab.linkedBrowser, [], () => { + let iframe = content.document.createElement("iframe"); + content.document.body.appendChild(iframe); + iframe.contentWindow.location = "https://example.com/"; + return iframe.browsingContext; + }); + }); + + ok( + !attached.has(window.browsingContext.id), + "no notification for the current window's chrome browsing context" + ); + ok( + !attached.has(tab.linkedBrowser.browsingContext.id), + "no notification for toplevel browsing context" + ); + ok( + attached.has(browsingContext.id), + "got notification for frame's browsing context" + ); + is( + attached.get(browsingContext.id).why, + "attach", + "expected reason for frame's browsing context" + ); + + BrowserTestUtils.removeTab(tab); +}); + +add_task(async function toplevelReplacedBy() { + let tab; + + let attached = await observeAttached(async () => { + tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots"); + }); + + const firstContext = tab.linkedBrowser.browsingContext; + ok( + attached.has(firstContext.id), + "got notification for initial toplevel browsing context" + ); + is( + attached.get(firstContext.id).why, + "attach", + "expected reason for initial toplevel browsing context" + ); + + attached = await observeAttached(async () => { + await loadURI(TEST_PATH); + }); + const secondContext = tab.linkedBrowser.browsingContext; + ok( + attached.has(secondContext.id), + "got notification for replaced toplevel browsing context" + ); + isnot(secondContext, firstContext, "browsing context to be replaced"); + is( + attached.get(secondContext.id).why, + "replace", + "expected reason for replaced toplevel browsing context" + ); + is( + secondContext.browserId, + firstContext.browserId, + "browserId has been kept" + ); + + attached = await observeAttached(async () => { + await loadURI("about:robots"); + }); + const thirdContext = tab.linkedBrowser.browsingContext; + ok( + attached.has(thirdContext.id), + "got notification for replaced toplevel browsing context" + ); + isnot(thirdContext, secondContext, "browsing context to be replaced"); + is( + attached.get(thirdContext.id).why, + "replace", + "expected reason for replaced toplevel browsing context" + ); + is( + thirdContext.browserId, + secondContext.browserId, + "browserId has been kept" + ); + + BrowserTestUtils.removeTab(tab); +}); diff --git a/docshell/test/browser/browser_browsing_context_discarded.js b/docshell/test/browser/browser_browsing_context_discarded.js new file mode 100644 index 0000000000..a300737d4f --- /dev/null +++ b/docshell/test/browser/browser_browsing_context_discarded.js @@ -0,0 +1,65 @@ +"use strict"; + +const TOPIC = "browsing-context-discarded"; + +async function observeDiscarded(browsingContexts, callback) { + let discarded = []; + + let promise = BrowserUtils.promiseObserved(TOPIC, subject => { + ok(BrowsingContext.isInstance(subject), "subject to be a BrowsingContext"); + discarded.push(subject); + + return browsingContexts.every(item => discarded.includes(item)); + }); + await callback(); + await promise; + + return discarded; +} + +add_task(async function toplevelForNewWindow() { + let win = await BrowserTestUtils.openNewBrowserWindow(); + let browsingContext = win.gBrowser.selectedBrowser.browsingContext; + + await observeDiscarded([win.browsingContext, browsingContext], async () => { + await BrowserTestUtils.closeWindow(win); + }); +}); + +add_task(async function toplevelForNewTab() { + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); + let browsingContext = tab.linkedBrowser.browsingContext; + + let discarded = await observeDiscarded([browsingContext], () => { + BrowserTestUtils.removeTab(tab); + }); + + ok( + !discarded.includes(window.browsingContext), + "no notification for the current window's chrome browsing context" + ); +}); + +add_task(async function subframe() { + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); + let browsingContext = await SpecialPowers.spawn(tab.linkedBrowser, [], () => { + let iframe = content.document.createElement("iframe"); + content.document.body.appendChild(iframe); + iframe.contentWindow.location = "https://example.com/"; + return iframe.browsingContext; + }); + + let discarded = await observeDiscarded([browsingContext], async () => { + await SpecialPowers.spawn(tab.linkedBrowser, [], () => { + let iframe = content.document.querySelector("iframe"); + iframe.remove(); + }); + }); + + ok( + !discarded.includes(tab.browsingContext), + "no notification for toplevel browsing context" + ); + + BrowserTestUtils.removeTab(tab); +}); diff --git a/docshell/test/browser/browser_bug1206879.js b/docshell/test/browser/browser_bug1206879.js new file mode 100644 index 0000000000..135e6c99ff --- /dev/null +++ b/docshell/test/browser/browser_bug1206879.js @@ -0,0 +1,50 @@ +add_task(async function() { + let url = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content/", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/" + ) + "file_bug1206879.html"; + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, true); + + let numLocationChanges = await SpecialPowers.spawn( + tab.linkedBrowser, + [], + async function() { + let webprogress = content.docShell.QueryInterface(Ci.nsIWebProgress); + let locationChangeCount = 0; + let listener = { + onLocationChange(aWebProgress, aRequest, aLocation, aFlags) { + info("onLocationChange: " + aLocation.spec); + locationChangeCount++; + this.resolve(); + }, + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), + }; + let locationPromise = new Promise((resolve, reject) => { + listener.resolve = resolve; + }); + webprogress.addProgressListener( + listener, + Ci.nsIWebProgress.NOTIFY_LOCATION + ); + + content.frames[0].history.pushState(null, null, "foo"); + + await locationPromise; + webprogress.removeProgressListener(listener); + + return locationChangeCount; + } + ); + + gBrowser.removeTab(tab); + is( + numLocationChanges, + 1, + "pushState with a different URI should cause a LocationChange event." + ); +}); diff --git a/docshell/test/browser/browser_bug1309900_crossProcessHistoryNavigation.js b/docshell/test/browser/browser_bug1309900_crossProcessHistoryNavigation.js new file mode 100644 index 0000000000..011811c74d --- /dev/null +++ b/docshell/test/browser/browser_bug1309900_crossProcessHistoryNavigation.js @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +add_task(async function runTests() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.navigation.requireUserInteraction", false]], + }); + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:about" + ); + + registerCleanupFunction(function() { + gBrowser.removeTab(tab); + }); + + let browser = tab.linkedBrowser; + + let loaded = BrowserTestUtils.browserLoaded(browser); + BrowserTestUtils.loadURI(browser, "about:config"); + let href = await loaded; + is(href, "about:config", "Check about:config loaded"); + + // Using a dummy onunload listener to disable the bfcache as that can prevent + // the test browser load detection mechanism from working. + loaded = BrowserTestUtils.browserLoaded(browser); + BrowserTestUtils.loadURI( + browser, + "data:text/html,<body%20onunload=''><iframe></iframe></body>" + ); + href = await loaded; + is( + href, + "data:text/html,<body%20onunload=''><iframe></iframe></body>", + "Check data URL loaded" + ); + + loaded = BrowserTestUtils.browserLoaded(browser); + browser.goBack(); + href = await loaded; + is(href, "about:config", "Check we've gone back to about:config"); + + loaded = BrowserTestUtils.browserLoaded(browser); + browser.goForward(); + href = await loaded; + is( + href, + "data:text/html,<body%20onunload=''><iframe></iframe></body>", + "Check we've gone forward to data URL" + ); +}); diff --git a/docshell/test/browser/browser_bug1328501.js b/docshell/test/browser/browser_bug1328501.js new file mode 100644 index 0000000000..32cc281f53 --- /dev/null +++ b/docshell/test/browser/browser_bug1328501.js @@ -0,0 +1,64 @@ +const HTML_URL = + "http://mochi.test:8888/browser/docshell/test/browser/file_bug1328501.html"; +const FRAME_URL = + "http://mochi.test:8888/browser/docshell/test/browser/file_bug1328501_frame.html"; +const FRAME_SCRIPT_URL = + "chrome://mochitests/content/browser/docshell/test/browser/file_bug1328501_framescript.js"; +add_task(async function testMultiFrameRestore() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.navigation.requireUserInteraction", false], + // Disable bfcache so that dummy_page.html doesn't enter there. + // The actual test page does already prevent bfcache and the test + // is just for http-on-opening-request handling in the child process. + ["browser.sessionhistory.max_total_viewers", 0], + ], + }); + await BrowserTestUtils.withNewTab({ gBrowser, url: HTML_URL }, async function( + browser + ) { + // Navigate 2 subframes and load about:blank. + let browserLoaded = BrowserTestUtils.browserLoaded(browser); + await SpecialPowers.spawn(browser, [FRAME_URL], async function(FRAME_URL) { + function frameLoaded(frame) { + frame.contentWindow.location = FRAME_URL; + return new Promise(r => (frame.onload = r)); + } + let frame1 = content.document.querySelector("#testFrame1"); + let frame2 = content.document.querySelector("#testFrame2"); + ok(frame1, "check found testFrame1"); + ok(frame2, "check found testFrame2"); + await frameLoaded(frame1); + await frameLoaded(frame2); + content.location = "dummy_page.html"; + }); + await browserLoaded; + + // Load a frame script to query nsIDOMWindow on "http-on-opening-request", + // which will force about:blank content viewers being created. + browser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false); + + // The frame script also forwards frames-loaded. + let framesLoaded = BrowserTestUtils.waitForMessage( + browser.messageManager, + "test:frames-loaded" + ); + + browser.goBack(); + await framesLoaded; + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise(r => setTimeout(r, 1000)); + await SpecialPowers.spawn(browser, [FRAME_URL], FRAME_URL => { + is( + content.document.querySelector("#testFrame1").contentWindow.location + .href, + FRAME_URL + ); + is( + content.document.querySelector("#testFrame2").contentWindow.location + .href, + FRAME_URL + ); + }); + }); +}); diff --git a/docshell/test/browser/browser_bug1347823.js b/docshell/test/browser/browser_bug1347823.js new file mode 100644 index 0000000000..9a6e14a76f --- /dev/null +++ b/docshell/test/browser/browser_bug1347823.js @@ -0,0 +1,85 @@ +/** + * Test that session history's expiration tracker would remove bfcache on + * expiration. + */ + +// With bfcache not expired. +add_task(async function testValidCache() { + // Make an unrealistic large timeout. + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.sessionhistory.contentViewerTimeout", 86400], + // If Fission is disabled, the pref is no-op. + ["fission.bfcacheInParent", true], + ], + }); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "data:text/html;charset=utf-8,pageA1" }, + async function(browser) { + // Make a simple modification for bfcache testing. + await SpecialPowers.spawn(browser, [], () => { + content.document.body.textContent = "modified"; + }); + + // Load a random page. + BrowserTestUtils.loadURI(browser, "data:text/html;charset=utf-8,pageA2"); + await BrowserTestUtils.browserLoaded(browser); + + // Go back and verify text content. + let awaitPageShow = BrowserTestUtils.waitForContentEvent( + browser, + "pageshow" + ); + browser.goBack(); + await awaitPageShow; + await SpecialPowers.spawn(browser, [], () => { + is(content.document.body.textContent, "modified"); + }); + } + ); +}); + +// With bfcache expired. +add_task(async function testExpiredCache() { + // Make bfcache timeout in 1 sec. + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.sessionhistory.contentViewerTimeout", 1], + // If Fission is disabled, the pref is no-op. + ["fission.bfcacheInParent", true], + ], + }); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "data:text/html;charset=utf-8,pageB1" }, + async function(browser) { + // Make a simple modification for bfcache testing. + await SpecialPowers.spawn(browser, [], () => { + content.document.body.textContent = "modified"; + }); + + // Load a random page. + BrowserTestUtils.loadURI(browser, "data:text/html;charset=utf-8,pageB2"); + await BrowserTestUtils.browserLoaded(browser); + + // Wait for 3 times of expiration timeout, hopefully it's evicted... + await SpecialPowers.spawn(browser, [], () => { + return new Promise(resolve => { + content.setTimeout(resolve, 5000); + }); + }); + + // Go back and verify text content. + let awaitPageShow = BrowserTestUtils.waitForContentEvent( + browser, + "pageshow" + ); + browser.goBack(); + await awaitPageShow; + await SpecialPowers.spawn(browser, [], () => { + is(content.document.body.textContent, "pageB1"); + }); + } + ); +}); diff --git a/docshell/test/browser/browser_bug134911.js b/docshell/test/browser/browser_bug134911.js new file mode 100644 index 0000000000..2fd3e82d4a --- /dev/null +++ b/docshell/test/browser/browser_bug134911.js @@ -0,0 +1,57 @@ +const TEXT = { + /* The test text decoded correctly as Shift_JIS */ + rightText: + "\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059", + + enteredText1: "The quick brown fox jumps over the lazy dog", + enteredText2: + "\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1", +}; + +function test() { + waitForExplicitFinish(); + + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + gBrowser.selectedTab = BrowserTestUtils.addTab( + gBrowser, + rootDir + "test-form_sjis.html" + ); + BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(afterOpen); +} + +function afterOpen() { + BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then( + afterChangeCharset + ); + + SpecialPowers.spawn(gBrowser.selectedBrowser, [TEXT], function(TEXT) { + content.document.getElementById("testtextarea").value = TEXT.enteredText1; + content.document.getElementById("testinput").value = TEXT.enteredText2; + }).then(() => { + /* Force the page encoding to Shift_JIS */ + BrowserForceEncodingDetection(); + }); +} + +function afterChangeCharset() { + SpecialPowers.spawn(gBrowser.selectedBrowser, [TEXT], function(TEXT) { + is( + content.document.getElementById("testpar").innerHTML, + TEXT.rightText, + "encoding successfully changed" + ); + is( + content.document.getElementById("testtextarea").value, + TEXT.enteredText1, + "text preserved in <textarea>" + ); + is( + content.document.getElementById("testinput").value, + TEXT.enteredText2, + "text preserved in <input>" + ); + }).then(() => { + gBrowser.removeCurrentTab(); + finish(); + }); +} diff --git a/docshell/test/browser/browser_bug1415918_beforeunload_options.js b/docshell/test/browser/browser_bug1415918_beforeunload_options.js new file mode 100644 index 0000000000..8ca5daf6c8 --- /dev/null +++ b/docshell/test/browser/browser_bug1415918_beforeunload_options.js @@ -0,0 +1,162 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com" +); + +const { PromptTestUtils } = ChromeUtils.import( + "resource://testing-common/PromptTestUtils.jsm" +); + +SimpleTest.requestFlakyTimeout("Needs to test a timeout"); + +function delay(msec) { + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + return new Promise(resolve => setTimeout(resolve, msec)); +} + +function allowNextNavigation(browser) { + return PromptTestUtils.handleNextPrompt( + browser, + { modalType: Services.prompt.MODAL_TYPE_CONTENT, promptType: "confirmEx" }, + { buttonNumClick: 0 } + ); +} + +function cancelNextNavigation(browser) { + return PromptTestUtils.handleNextPrompt( + browser, + { modalType: Services.prompt.MODAL_TYPE_CONTENT, promptType: "confirmEx" }, + { buttonNumClick: 1 } + ); +} + +add_task(async function test() { + await SpecialPowers.pushPrefEnv({ + set: [["dom.require_user_interaction_for_beforeunload", false]], + }); + + const permitUnloadTimeout = Services.prefs.getIntPref( + "dom.beforeunload_timeout_ms" + ); + + let url = TEST_PATH + "dummy_page.html"; + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url); + let browser = tab.linkedBrowser; + + await SpecialPowers.spawn(browser.browsingContext, [], () => { + content.addEventListener("beforeunload", event => { + event.preventDefault(); + }); + }); + + /* + * Check condition where beforeunload handlers request a prompt. + */ + + // Prompt is shown, user clicks OK. + + let promptShownPromise = allowNextNavigation(browser); + ok(browser.permitUnload().permitUnload, "permit unload should be true"); + await promptShownPromise; + + // Prompt is shown, user clicks CANCEL. + promptShownPromise = cancelNextNavigation(browser); + ok(!browser.permitUnload().permitUnload, "permit unload should be false"); + await promptShownPromise; + + // Prompt is not shown, don't permit unload. + let promptShown = false; + let shownCallback = () => { + promptShown = true; + }; + + browser.addEventListener("DOMWillOpenModalDialog", shownCallback); + ok( + !browser.permitUnload("dontUnload").permitUnload, + "permit unload should be false" + ); + ok(!promptShown, "prompt should not have been displayed"); + + // Prompt is not shown, permit unload. + promptShown = false; + ok( + browser.permitUnload("unload").permitUnload, + "permit unload should be true" + ); + ok(!promptShown, "prompt should not have been displayed"); + browser.removeEventListener("DOMWillOpenModalDialog", shownCallback); + + promptShownPromise = PromptTestUtils.waitForPrompt(browser, { + modalType: Services.prompt.MODAL_TYPE_CONTENT, + promptType: "confirmEx", + }); + + let promptDismissed = false; + let closedCallback = () => { + promptDismissed = true; + }; + + browser.addEventListener("DOMModalDialogClosed", closedCallback); + + let promise = browser.asyncPermitUnload(); + + let promiseResolved = false; + promise.then(() => { + promiseResolved = true; + }); + + let dialog = await promptShownPromise; + ok(!promiseResolved, "Should not have resolved promise yet"); + + await delay(permitUnloadTimeout * 1.5); + + ok(!promptDismissed, "Should not have dismissed prompt yet"); + ok(!promiseResolved, "Should not have resolved promise yet"); + + await PromptTestUtils.handlePrompt(dialog, { buttonNumClick: 1 }); + + let { permitUnload } = await promise; + ok(promptDismissed, "Should have dismissed prompt"); + ok(!permitUnload, "Should not have permitted unload"); + + browser.removeEventListener("DOMModalDialogClosed", closedCallback); + + promptShownPromise = allowNextNavigation(browser); + + /* + * Check condition where no one requests a prompt. In all cases, + * permitUnload should be true, and all handlers fired. + */ + url += "?1"; + BrowserTestUtils.loadURI(browser, url); + await BrowserTestUtils.browserLoaded(browser, false, url); + await promptShownPromise; + + promptShown = false; + browser.addEventListener("DOMWillOpenModalDialog", shownCallback); + + ok(browser.permitUnload().permitUnload, "permit unload should be true"); + ok(!promptShown, "prompt should not have been displayed"); + + promptShown = false; + ok( + browser.permitUnload("dontUnload").permitUnload, + "permit unload should be true" + ); + ok(!promptShown, "prompt should not have been displayed"); + + promptShown = false; + ok( + browser.permitUnload("unload").permitUnload, + "permit unload should be true" + ); + ok(!promptShown, "prompt should not have been displayed"); + + browser.removeEventListener("DOMWillOpenModalDialog", shownCallback); + + await BrowserTestUtils.removeTab(tab, { skipPermitUnload: true }); +}); diff --git a/docshell/test/browser/browser_bug1543077-3.js b/docshell/test/browser/browser_bug1543077-3.js new file mode 100644 index 0000000000..7cef4aef10 --- /dev/null +++ b/docshell/test/browser/browser_bug1543077-3.js @@ -0,0 +1,46 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug1543077-3.html", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u3042"), + 136, + "Parent doc should be ISO-2022-JP initially" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u3042"), + 92, + "Child doc should be ISO-2022-JP initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u3042"), + 136, + "Parent doc should decode as ISO-2022-JP subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u3042"), + 92, + "Child doc should decode as ISO-2022-JP subsequently" + ); + + is( + content.document.characterSet, + "ISO-2022-JP", + "Parent doc should report ISO-2022-JP subsequently" + ); + is( + content.frames[0].document.characterSet, + "ISO-2022-JP", + "Child doc should report ISO-2022-JP subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug1594938.js b/docshell/test/browser/browser_bug1594938.js new file mode 100644 index 0000000000..5a9d4814ba --- /dev/null +++ b/docshell/test/browser/browser_bug1594938.js @@ -0,0 +1,99 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test for Bug 1594938 + * + * If a session history listener blocks reloads we shouldn't crash. + */ + +add_task(async function test() { + await BrowserTestUtils.withNewTab( + { gBrowser, url: "https://example.com/" }, + async function(browser) { + if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) { + await SpecialPowers.spawn(browser, [], async function() { + let history = this.content.docShell.QueryInterface( + Ci.nsIWebNavigation + ).sessionHistory; + + let testDone = {}; + testDone.promise = new Promise(resolve => { + testDone.resolve = resolve; + }); + + let listenerCalled = false; + let listener = { + OnHistoryNewEntry: aNewURI => {}, + OnHistoryReload: () => { + listenerCalled = true; + this.content.setTimeout(() => { + testDone.resolve(); + }); + return false; + }, + OnHistoryGotoIndex: () => {}, + OnHistoryPurge: () => {}, + OnHistoryReplaceEntry: () => {}, + + QueryInterface: ChromeUtils.generateQI([ + "nsISHistoryListener", + "nsISupportsWeakReference", + ]), + }; + + history.legacySHistory.addSHistoryListener(listener); + + history.reload(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE); + await testDone.promise; + + Assert.ok(listenerCalled, "reloads were blocked"); + + history.legacySHistory.removeSHistoryListener(listener); + }); + + return; + } + + let history = browser.browsingContext.sessionHistory; + + let testDone = {}; + testDone.promise = new Promise(resolve => { + testDone.resolve = resolve; + }); + + let listenerCalled = false; + let listener = { + OnHistoryNewEntry: aNewURI => {}, + OnHistoryReload: () => { + listenerCalled = true; + setTimeout(() => { + testDone.resolve(); + }); + return false; + }, + OnHistoryGotoIndex: () => {}, + OnHistoryPurge: () => {}, + OnHistoryReplaceEntry: () => {}, + + QueryInterface: ChromeUtils.generateQI([ + "nsISHistoryListener", + "nsISupportsWeakReference", + ]), + }; + + history.addSHistoryListener(listener); + + await SpecialPowers.spawn(browser, [], () => { + let history = this.content.docShell.QueryInterface(Ci.nsIWebNavigation) + .sessionHistory; + history.reload(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE); + }); + await testDone.promise; + + Assert.ok(listenerCalled, "reloads were blocked"); + + history.removeSHistoryListener(listener); + } + ); +}); diff --git a/docshell/test/browser/browser_bug1622420.js b/docshell/test/browser/browser_bug1622420.js new file mode 100644 index 0000000000..81026611e4 --- /dev/null +++ b/docshell/test/browser/browser_bug1622420.js @@ -0,0 +1,31 @@ +const ACTOR = "Bug1622420"; + +add_task(async function test() { + let base = getRootDirectory(gTestPath).slice(0, -1); + ChromeUtils.registerWindowActor(ACTOR, { + allFrames: true, + child: { + esModuleURI: `${base}/Bug1622420Child.sys.mjs`, + }, + }); + + registerCleanupFunction(async () => { + gBrowser.removeTab(tab); + + ChromeUtils.unregisterWindowActor(ACTOR); + }); + + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/docshell/test/browser/file_bug1622420.html" + ); + let childBC = tab.linkedBrowser.browsingContext.children[0]; + let success = await childBC.currentWindowGlobal + .getActor(ACTOR) + .sendQuery("hasWindowContextForTopBC"); + ok( + success, + "Should have a WindowContext for the top BrowsingContext in the process of a child BrowsingContext" + ); +}); diff --git a/docshell/test/browser/browser_bug1648464-1.js b/docshell/test/browser/browser_bug1648464-1.js new file mode 100644 index 0000000000..c2a8093a3d --- /dev/null +++ b/docshell/test/browser/browser_bug1648464-1.js @@ -0,0 +1,46 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug1648464-1.html", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u00A4"), + 146, + "Parent doc should be windows-1252 initially" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u00A4"), + 95, + "Child doc should be windows-1252 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u3042"), + 146, + "Parent doc should decode as EUC-JP subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u3042"), + 95, + "Child doc should decode as EUC-JP subsequently" + ); + + is( + content.document.characterSet, + "EUC-JP", + "Parent doc should report EUC-JP subsequently" + ); + is( + content.frames[0].document.characterSet, + "EUC-JP", + "Child doc should report EUC-JP subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug1673702.js b/docshell/test/browser/browser_bug1673702.js new file mode 100644 index 0000000000..3be350b0d7 --- /dev/null +++ b/docshell/test/browser/browser_bug1673702.js @@ -0,0 +1,26 @@ +const DUMMY = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/docshell/test/browser/dummy_page.html"; +const JSON = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/browser/docshell/test/browser/file_bug1673702.json"; + +add_task(async function test_backAndReload() { + await BrowserTestUtils.withNewTab({ gBrowser, url: DUMMY }, async function( + browser + ) { + info("Start JSON load."); + BrowserTestUtils.loadURI(browser, JSON); + await BrowserTestUtils.waitForLocationChange(gBrowser, JSON); + + info("JSON load has started, go back."); + browser.goBack(); + await BrowserTestUtils.browserStopped(browser); + + info("Reload."); + BrowserReload(); + await BrowserTestUtils.waitForLocationChange(gBrowser); + + is(browser.documentURI.spec, DUMMY); + }); +}); diff --git a/docshell/test/browser/browser_bug1674464.js b/docshell/test/browser/browser_bug1674464.js new file mode 100644 index 0000000000..1b78802cc0 --- /dev/null +++ b/docshell/test/browser/browser_bug1674464.js @@ -0,0 +1,37 @@ +const DUMMY_1 = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/docshell/test/browser/dummy_page.html"; +const DUMMY_2 = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/browser/docshell/test/browser/dummy_page.html"; + +add_task(async function test_backAndReload() { + await BrowserTestUtils.withNewTab({ gBrowser, url: DUMMY_1 }, async function( + browser + ) { + await BrowserTestUtils.crashFrame(browser); + + info("Start second load."); + BrowserTestUtils.loadURI(browser, DUMMY_2); + await BrowserTestUtils.waitForLocationChange(gBrowser, DUMMY_2); + + browser.goBack(); + await BrowserTestUtils.waitForLocationChange(gBrowser); + + is( + browser.browsingContext.childSessionHistory.index, + 0, + "We should have gone back to the first page" + ); + is( + browser.browsingContext.childSessionHistory.count, + 2, + "If a tab crashes after a load has finished we shouldn't have an entry for about:tabcrashed" + ); + is( + browser.documentURI.spec, + DUMMY_1, + "If a tab crashes after a load has finished we shouldn't have an entry for about:tabcrashed" + ); + }); +}); diff --git a/docshell/test/browser/browser_bug1688368-1.js b/docshell/test/browser/browser_bug1688368-1.js new file mode 100644 index 0000000000..04fc3dd9a8 --- /dev/null +++ b/docshell/test/browser/browser_bug1688368-1.js @@ -0,0 +1,24 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug1688368-1.sjs", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.body.textContent.indexOf("â"), + 0, + "Doc should be windows-1252 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.body.textContent.indexOf("☃"), + 0, + "Doc should be UTF-8 subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug1691153.js b/docshell/test/browser/browser_bug1691153.js new file mode 100644 index 0000000000..b6d465393f --- /dev/null +++ b/docshell/test/browser/browser_bug1691153.js @@ -0,0 +1,73 @@ +"use strict"; + +add_task(async () => { + const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com" + ); + + const HTML_URI = TEST_PATH + "file_bug1691153.html"; + + // Opening the page that contains the iframe + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); + let browser = tab.linkedBrowser; + let browserLoaded = BrowserTestUtils.browserLoaded( + browser, + true, + HTML_URI, + true + ); + info("new tab loaded"); + + BrowserTestUtils.loadURI(browser, HTML_URI); + await browserLoaded; + info("The test page has loaded!"); + + let first_message_promise = SpecialPowers.spawn( + browser, + [], + async function() { + let blobPromise = new Promise((resolve, reject) => { + content.addEventListener("message", event => { + if (event.data.bloburl) { + info("Sanity check: recvd blob URL as " + event.data.bloburl); + resolve(event.data.bloburl); + } + }); + }); + content.postMessage("getblob", "*"); + return blobPromise; + } + ); + info("The test page has loaded!"); + let blob_url = await first_message_promise; + + Assert.ok(blob_url.startsWith("blob:"), "Sanity check: recvd blob"); + info(`Received blob URL message from content: ${blob_url}`); + // try to open the blob in a new tab, manually created by the user + let tab2 = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + blob_url, + true, + false, + true + ); + + let principal = gBrowser.selectedTab.linkedBrowser._contentPrincipal; + Assert.ok( + !principal.isSystemPrincipal, + "Newly opened blob shouldn't be Systemprincipal" + ); + Assert.ok( + !principal.isExpandedPrincipal, + "Newly opened blob shouldn't be ExpandedPrincipal" + ); + Assert.ok( + principal.isContentPrincipal, + "Newly opened blob tab should be ContentPrincipal" + ); + + BrowserTestUtils.removeTab(tab); + BrowserTestUtils.removeTab(tab2); +}); diff --git a/docshell/test/browser/browser_bug1705872.js b/docshell/test/browser/browser_bug1705872.js new file mode 100644 index 0000000000..35a3f66d13 --- /dev/null +++ b/docshell/test/browser/browser_bug1705872.js @@ -0,0 +1,74 @@ +"use strict"; + +async function doLoadAndGoBack(browser, ext) { + let loaded = BrowserTestUtils.browserLoaded(browser); + BrowserTestUtils.loadURI(browser, "https://example.com/"); + await ext.awaitMessage("redir-handled"); + await loaded; + + let pageShownPromise = BrowserTestUtils.waitForContentEvent( + browser, + "pageshow", + true + ); + await SpecialPowers.spawn(browser, [], () => { + content.history.back(); + }); + return pageShownPromise; +} + +add_task(async function test_back() { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["webRequest", "webRequestBlocking", "https://example.com/"], + web_accessible_resources: ["test.html"], + }, + files: { + "test.html": + "<!DOCTYPE html><html><head><title>Test add-on</title></head><body></body></html>", + }, + background: () => { + let { browser } = this; + browser.webRequest.onHeadersReceived.addListener( + details => { + if (details.statusCode != 200) { + return undefined; + } + browser.test.sendMessage("redir-handled"); + return { redirectUrl: browser.runtime.getURL("test.html") }; + }, + { + urls: ["https://example.com/"], + types: ["main_frame"], + }, + ["blocking"] + ); + }, + }); + + await extension.startup(); + + await BrowserTestUtils.withNewTab("about:home", async function(browser) { + await doLoadAndGoBack(browser, extension); + + await SpecialPowers.spawn(browser, [], () => { + is( + content.document.documentURI, + "about:home", + "Gone back to the right page" + ); + }); + + await doLoadAndGoBack(browser, extension); + + await SpecialPowers.spawn(browser, [], () => { + is( + content.document.documentURI, + "about:home", + "Gone back to the right page" + ); + }); + }); + + await extension.unload(); +}); diff --git a/docshell/test/browser/browser_bug1716290-1.js b/docshell/test/browser/browser_bug1716290-1.js new file mode 100644 index 0000000000..48add6f0eb --- /dev/null +++ b/docshell/test/browser/browser_bug1716290-1.js @@ -0,0 +1,24 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug1716290-1.sjs", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.characterSet, + "Shift_JIS", + "Doc should report Shift_JIS initially" + ); +} + +function afterChangeCharset() { + is( + content.document.characterSet, + "windows-1252", + "Doc should report windows-1252 subsequently (detector should override header)" + ); +} diff --git a/docshell/test/browser/browser_bug1716290-2.js b/docshell/test/browser/browser_bug1716290-2.js new file mode 100644 index 0000000000..a33c61f076 --- /dev/null +++ b/docshell/test/browser/browser_bug1716290-2.js @@ -0,0 +1,24 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug1716290-2.sjs", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.characterSet, + "Shift_JIS", + "Doc should report Shift_JIS initially" + ); +} + +function afterChangeCharset() { + is( + content.document.characterSet, + "windows-1252", + "Doc should report windows-1252 subsequently (detector should override meta resolving to the replacement encoding)" + ); +} diff --git a/docshell/test/browser/browser_bug1716290-3.js b/docshell/test/browser/browser_bug1716290-3.js new file mode 100644 index 0000000000..f7e6ca5bbd --- /dev/null +++ b/docshell/test/browser/browser_bug1716290-3.js @@ -0,0 +1,24 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug1716290-3.sjs", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.characterSet, + "Shift_JIS", + "Doc should report Shift_JIS initially" + ); +} + +function afterChangeCharset() { + is( + content.document.characterSet, + "replacement", + "Doc should report replacement subsequently (non-ASCII-compatible HTTP header should override detector)" + ); +} diff --git a/docshell/test/browser/browser_bug1716290-4.js b/docshell/test/browser/browser_bug1716290-4.js new file mode 100644 index 0000000000..ab0d46d7ff --- /dev/null +++ b/docshell/test/browser/browser_bug1716290-4.js @@ -0,0 +1,24 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug1716290-4.sjs", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.characterSet, + "Shift_JIS", + "Doc should report Shift_JIS initially" + ); +} + +function afterChangeCharset() { + is( + content.document.characterSet, + "UTF-16BE", + "Doc should report UTF-16BE subsequently (BOM should override detector)" + ); +} diff --git a/docshell/test/browser/browser_bug1719178.js b/docshell/test/browser/browser_bug1719178.js new file mode 100644 index 0000000000..ec42ec79d5 --- /dev/null +++ b/docshell/test/browser/browser_bug1719178.js @@ -0,0 +1,34 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; +SimpleTest.requestFlakyTimeout( + "The test needs to let objects die asynchronously." +); + +add_task(async function test_accessing_shistory() { + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:preferences" + ); + let sh = tab.linkedBrowser.browsingContext.sessionHistory; + ok(sh, "Should have SessionHistory object"); + gBrowser.removeTab(tab); + tab = null; + for (let i = 0; i < 5; ++i) { + SpecialPowers.Services.obs.notifyObservers( + null, + "memory-pressure", + "heap-minimize" + ); + SpecialPowers.DOMWindowUtils.garbageCollect(); + await new Promise(function(r) { + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + setTimeout(r, 50); + }); + } + + try { + sh.reloadCurrentEntry(); + } catch (ex) {} + ok(true, "This test shouldn't crash."); +}); diff --git a/docshell/test/browser/browser_bug1736248-1.js b/docshell/test/browser/browser_bug1736248-1.js new file mode 100644 index 0000000000..d9cbe4fd85 --- /dev/null +++ b/docshell/test/browser/browser_bug1736248-1.js @@ -0,0 +1,34 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug1736248-1.html", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u00C3"), + 1064, + "Doc should be windows-1252 initially" + ); + is( + content.document.characterSet, + "windows-1252", + "Doc should report windows-1252 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u00E4"), + 1064, + "Doc should be UTF-8 subsequently" + ); + is( + content.document.characterSet, + "UTF-8", + "Doc should report UTF-8 subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug1757005.js b/docshell/test/browser/browser_bug1757005.js new file mode 100644 index 0000000000..873ae15d21 --- /dev/null +++ b/docshell/test/browser/browser_bug1757005.js @@ -0,0 +1,73 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function() { + // (1) Load one page with bfcache disabled and another one with bfcache enabled. + // (2) Check that BrowsingContext.getCurrentTopByBrowserId(browserId) returns + // the expected browsing context both in the parent process and in the child process. + // (3) Go back and then forward + // (4) Run the same checks as in step 2 again. + + let url1 = "data:text/html,<body onunload='/* disable bfcache */'>"; + let url2 = "data:text/html,page2"; + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: url1, + }, + async function(browser) { + info("Initial load"); + + let loaded = BrowserTestUtils.browserLoaded(browser); + BrowserTestUtils.loadURI(browser, url2); + await loaded; + info("Second page loaded"); + + let browserId = browser.browserId; + ok(!!browser.browsingContext, "Should have a BrowsingContext. (1)"); + is( + BrowsingContext.getCurrentTopByBrowserId(browserId), + browser.browsingContext, + "Should get the correct browsingContext(1)" + ); + + await ContentTask.spawn(browser, browserId, async function(browserId) { + Assert.ok( + BrowsingContext.getCurrentTopByBrowserId(browserId) == + docShell.browsingContext + ); + Assert.ok(docShell.browsingContext.browserId == browserId); + }); + + let awaitPageShow = BrowserTestUtils.waitForContentEvent( + browser, + "pageshow" + ); + browser.goBack(); + await awaitPageShow; + info("Back"); + + awaitPageShow = BrowserTestUtils.waitForContentEvent(browser, "pageshow"); + browser.goForward(); + await awaitPageShow; + info("Forward"); + + ok(!!browser.browsingContext, "Should have a BrowsingContext. (2)"); + is( + BrowsingContext.getCurrentTopByBrowserId(browserId), + browser.browsingContext, + "Should get the correct BrowsingContext. (2)" + ); + + await ContentTask.spawn(browser, browserId, async function(browserId) { + Assert.ok( + BrowsingContext.getCurrentTopByBrowserId(browserId) == + docShell.browsingContext + ); + Assert.ok(docShell.browsingContext.browserId == browserId); + }); + } + ); +}); diff --git a/docshell/test/browser/browser_bug1769189.js b/docshell/test/browser/browser_bug1769189.js new file mode 100644 index 0000000000..5be78c84f3 --- /dev/null +++ b/docshell/test/browser/browser_bug1769189.js @@ -0,0 +1,33 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function test_beforeUnload_and_replaceState() { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: + "data:text/html,<script>window.addEventListener('beforeunload', () => { window.history.replaceState(true, ''); });</script>", + }, + async function(browser) { + let initialState = await SpecialPowers.spawn(browser, [], () => { + return content.history.state; + }); + + is(initialState, null, "history.state should be initially null."); + + let awaitPageShow = BrowserTestUtils.waitForContentEvent( + browser, + "pageshow" + ); + BrowserReload(); + await awaitPageShow; + + let updatedState = await SpecialPowers.spawn(browser, [], () => { + return content.history.state; + }); + is(updatedState, true, "history.state should have been updated."); + } + ); +}); diff --git a/docshell/test/browser/browser_bug1798780.js b/docshell/test/browser/browser_bug1798780.js new file mode 100644 index 0000000000..4da531be00 --- /dev/null +++ b/docshell/test/browser/browser_bug1798780.js @@ -0,0 +1,51 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// The test loads an initial page and then another page which does enough +// fragment navigations so that when going back to the initial page and then +// forward to the last page, the initial page is evicted from the bfcache. +add_task(async function testBFCacheEviction() { + // Make an unrealistic large timeout. + await SpecialPowers.pushPrefEnv({ + set: [["browser.sessionhistory.contentViewerTimeout", 86400]], + }); + + const uri = "data:text/html,initial page"; + const uri2 = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" + ) + "dummy_page.html"; + + await BrowserTestUtils.withNewTab({ gBrowser, url: uri }, async function( + browser + ) { + BrowserTestUtils.loadURI(browser, uri2); + await BrowserTestUtils.browserLoaded(browser); + + let awaitPageShow = BrowserTestUtils.waitForContentEvent( + browser, + "pageshow" + ); + await SpecialPowers.spawn(browser, [], async function() { + content.location.hash = "1"; + content.location.hash = "2"; + content.location.hash = "3"; + content.history.go(-4); + }); + + await awaitPageShow; + + let awaitPageShow2 = BrowserTestUtils.waitForContentEvent( + browser, + "pageshow" + ); + await SpecialPowers.spawn(browser, [], async function() { + content.history.go(4); + }); + await awaitPageShow2; + ok(true, "Didn't time out."); + }); +}); diff --git a/docshell/test/browser/browser_bug234628-1.js b/docshell/test/browser/browser_bug234628-1.js new file mode 100644 index 0000000000..566da65bca --- /dev/null +++ b/docshell/test/browser/browser_bug234628-1.js @@ -0,0 +1,47 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-1.html", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 129, + "Parent doc should be windows-1252 initially" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 85, + "Child doc should be windows-1252 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 129, + "Parent doc should be windows-1252 subsequently" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 85, + "Child doc should be windows-1252 subsequently" + ); + + is( + content.document.characterSet, + "windows-1252", + "Parent doc should report windows-1252 subsequently" + ); + is( + content.frames[0].document.characterSet, + "windows-1252", + "Child doc should report windows-1252 subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug234628-10.js b/docshell/test/browser/browser_bug234628-10.js new file mode 100644 index 0000000000..8fb51cf27c --- /dev/null +++ b/docshell/test/browser/browser_bug234628-10.js @@ -0,0 +1,46 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-10.html", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 151, + "Parent doc should be windows-1252 initially" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 71, + "Child doc should be utf-8 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 151, + "Parent doc should be windows-1252 initially" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 71, + "Child doc should decode as utf-8 subsequently" + ); + + is( + content.document.characterSet, + "windows-1252", + "Parent doc should report windows-1252 subsequently" + ); + is( + content.frames[0].document.characterSet, + "UTF-8", + "Child doc should report UTF-8 subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug234628-11.js b/docshell/test/browser/browser_bug234628-11.js new file mode 100644 index 0000000000..d11645ff76 --- /dev/null +++ b/docshell/test/browser/browser_bug234628-11.js @@ -0,0 +1,46 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-11.html", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 193, + "Parent doc should be windows-1252 initially" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 107, + "Child doc should be utf-8 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 193, + "Parent doc should be windows-1252 subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 107, + "Child doc should decode as utf-8 subsequently" + ); + + is( + content.document.characterSet, + "windows-1252", + "Parent doc should report windows-1252 subsequently" + ); + is( + content.frames[0].document.characterSet, + "UTF-8", + "Child doc should report UTF-8 subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug234628-2.js b/docshell/test/browser/browser_bug234628-2.js new file mode 100644 index 0000000000..da93dc2ac2 --- /dev/null +++ b/docshell/test/browser/browser_bug234628-2.js @@ -0,0 +1,49 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-2.html", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 129, + "Parent doc should be windows-1252 initially" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf( + "\u00E2\u201A\u00AC" + ), + 78, + "Child doc should be windows-1252 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 129, + "Parent doc should be windows-1252 subsequently" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 78, + "Child doc should be UTF-8 subsequently" + ); + + is( + content.document.characterSet, + "windows-1252", + "Parent doc should report windows-1252 subsequently" + ); + is( + content.frames[0].document.characterSet, + "UTF-8", + "Child doc should report UTF-8 subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug234628-3.js b/docshell/test/browser/browser_bug234628-3.js new file mode 100644 index 0000000000..8a143b51a6 --- /dev/null +++ b/docshell/test/browser/browser_bug234628-3.js @@ -0,0 +1,47 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-3.html", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 118, + "Parent doc should be windows-1252 initially" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 73, + "Child doc should be utf-8 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 118, + "Parent doc should be windows-1252 subsequently" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 73, + "Child doc should be utf-8 subsequently" + ); + + is( + content.document.characterSet, + "windows-1252", + "Parent doc should report windows-1252 subsequently" + ); + is( + content.frames[0].document.characterSet, + "UTF-8", + "Child doc should report UTF-8 subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug234628-4.js b/docshell/test/browser/browser_bug234628-4.js new file mode 100644 index 0000000000..19ec0f8dbf --- /dev/null +++ b/docshell/test/browser/browser_bug234628-4.js @@ -0,0 +1,46 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-4.html", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 132, + "Parent doc should be windows-1252 initially" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 79, + "Child doc should be utf-8 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 132, + "Parent doc should decode as windows-1252 subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 79, + "Child doc should decode as utf-8 subsequently" + ); + + is( + content.document.characterSet, + "windows-1252", + "Parent doc should report windows-1252 subsequently" + ); + is( + content.frames[0].document.characterSet, + "UTF-8", + "Child doc should report UTF-8 subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug234628-5.js b/docshell/test/browser/browser_bug234628-5.js new file mode 100644 index 0000000000..77753ed78d --- /dev/null +++ b/docshell/test/browser/browser_bug234628-5.js @@ -0,0 +1,46 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-5.html", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 146, + "Parent doc should be windows-1252 initially" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 87, + "Child doc should be utf-16 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 146, + "Parent doc should be windows-1252 subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 87, + "Child doc should decode as utf-16 subsequently" + ); + + is( + content.document.characterSet, + "windows-1252", + "Parent doc should report windows-1252 subsequently" + ); + is( + content.frames[0].document.characterSet, + "UTF-16LE", + "Child doc should report UTF-16LE subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug234628-6.js b/docshell/test/browser/browser_bug234628-6.js new file mode 100644 index 0000000000..88ff6c1a82 --- /dev/null +++ b/docshell/test/browser/browser_bug234628-6.js @@ -0,0 +1,47 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-6.html", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 190, + "Parent doc should be windows-1252 initially" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 109, + "Child doc should be utf-16 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 190, + "Parent doc should be windows-1252 subsequently" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 109, + "Child doc should be utf-16 subsequently" + ); + + is( + content.document.characterSet, + "windows-1252", + "Parent doc should report windows-1252 subsequently" + ); + is( + content.frames[0].document.characterSet, + "UTF-16BE", + "Child doc should report UTF-16 subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug234628-8.js b/docshell/test/browser/browser_bug234628-8.js new file mode 100644 index 0000000000..024a3d4d64 --- /dev/null +++ b/docshell/test/browser/browser_bug234628-8.js @@ -0,0 +1,18 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetCheck(rootDir + "file_bug234628-8.html", afterOpen); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u0402"), + 156, + "Parent doc should be windows-1251" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u0402"), + 99, + "Child doc should be windows-1251" + ); +} diff --git a/docshell/test/browser/browser_bug234628-9.js b/docshell/test/browser/browser_bug234628-9.js new file mode 100644 index 0000000000..ceb7dc4e63 --- /dev/null +++ b/docshell/test/browser/browser_bug234628-9.js @@ -0,0 +1,18 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetCheck(rootDir + "file_bug234628-9.html", afterOpen); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 145, + "Parent doc should be UTF-16" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 96, + "Child doc should be windows-1252" + ); +} diff --git a/docshell/test/browser/browser_bug349769.js b/docshell/test/browser/browser_bug349769.js new file mode 100644 index 0000000000..6fde42e876 --- /dev/null +++ b/docshell/test/browser/browser_bug349769.js @@ -0,0 +1,72 @@ +add_task(async function test() { + const uris = [undefined, "about:blank"]; + + function checkContentProcess(newBrowser, uri) { + return ContentTask.spawn(newBrowser, [uri], async function(uri) { + var prin = content.document.nodePrincipal; + Assert.notEqual( + prin, + null, + "Loaded principal must not be null when adding " + uri + ); + Assert.notEqual( + prin, + undefined, + "Loaded principal must not be undefined when loading " + uri + ); + + Assert.equal( + prin.isSystemPrincipal, + false, + "Loaded principal must not be system when loading " + uri + ); + }); + } + + for (var uri of uris) { + await BrowserTestUtils.withNewTab({ gBrowser }, async function(newBrowser) { + let loadedPromise = BrowserTestUtils.browserLoaded(newBrowser); + BrowserTestUtils.loadURI(newBrowser, uri); + + var prin = newBrowser.contentPrincipal; + isnot( + prin, + null, + "Forced principal must not be null when loading " + uri + ); + isnot( + prin, + undefined, + "Forced principal must not be undefined when loading " + uri + ); + is( + prin.isSystemPrincipal, + false, + "Forced principal must not be system when loading " + uri + ); + + // Belt-and-suspenders e10s check: make sure that the same checks hold + // true in the content process. + await checkContentProcess(newBrowser, uri); + + await loadedPromise; + + prin = newBrowser.contentPrincipal; + isnot(prin, null, "Loaded principal must not be null when adding " + uri); + isnot( + prin, + undefined, + "Loaded principal must not be undefined when loading " + uri + ); + is( + prin.isSystemPrincipal, + false, + "Loaded principal must not be system when loading " + uri + ); + + // Belt-and-suspenders e10s check: make sure that the same checks hold + // true in the content process. + await checkContentProcess(newBrowser, uri); + }); + } +}); diff --git a/docshell/test/browser/browser_bug388121-1.js b/docshell/test/browser/browser_bug388121-1.js new file mode 100644 index 0000000000..6206e158af --- /dev/null +++ b/docshell/test/browser/browser_bug388121-1.js @@ -0,0 +1,22 @@ +add_task(async function test() { + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function(newBrowser) { + await SpecialPowers.spawn(newBrowser, [], async function() { + var prin = content.document.nodePrincipal; + Assert.notEqual(prin, null, "Loaded principal must not be null"); + Assert.notEqual( + prin, + undefined, + "Loaded principal must not be undefined" + ); + + Assert.equal( + prin.isSystemPrincipal, + false, + "Loaded principal must not be system" + ); + }); + } + ); +}); diff --git a/docshell/test/browser/browser_bug388121-2.js b/docshell/test/browser/browser_bug388121-2.js new file mode 100644 index 0000000000..695ff1079c --- /dev/null +++ b/docshell/test/browser/browser_bug388121-2.js @@ -0,0 +1,74 @@ +function test() { + waitForExplicitFinish(); + + var w; + var iteration = 1; + const uris = ["", "about:blank"]; + var uri; + var origWgp; + + function testLoad() { + let wgp = w.gBrowser.selectedBrowser.browsingContext.currentWindowGlobal; + if (wgp == origWgp) { + // Go back to polling + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + setTimeout(testLoad, 10); + return; + } + var prin = wgp.documentPrincipal; + isnot(prin, null, "Loaded principal must not be null when adding " + uri); + isnot( + prin, + undefined, + "Loaded principal must not be undefined when loading " + uri + ); + is( + prin.isSystemPrincipal, + false, + "Loaded principal must not be system when loading " + uri + ); + w.close(); + + if (iteration == uris.length) { + finish(); + } else { + ++iteration; + doTest(); + } + } + + function doTest() { + uri = uris[iteration - 1]; + window.open(uri, "_blank", "width=10,height=10,noopener"); + w = Services.wm.getMostRecentWindow("navigator:browser"); + origWgp = w.gBrowser.selectedBrowser.browsingContext.currentWindowGlobal; + var prin = origWgp.documentPrincipal; + if (!uri) { + uri = undefined; + } + isnot(prin, null, "Forced principal must not be null when loading " + uri); + isnot( + prin, + undefined, + "Forced principal must not be undefined when loading " + uri + ); + is( + prin.isSystemPrincipal, + false, + "Forced principal must not be system when loading " + uri + ); + if (uri == undefined) { + // No actual load here, so just move along. + w.close(); + ++iteration; + doTest(); + } else { + // Need to poll, because load listeners on the content window won't + // survive the load. + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + setTimeout(testLoad, 10); + } + } + + doTest(); +} diff --git a/docshell/test/browser/browser_bug420605.js b/docshell/test/browser/browser_bug420605.js new file mode 100644 index 0000000000..13e3695eae --- /dev/null +++ b/docshell/test/browser/browser_bug420605.js @@ -0,0 +1,133 @@ +/* Test for Bug 420605 + * https://bugzilla.mozilla.org/show_bug.cgi?id=420605 + */ + +const { PlacesTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PlacesTestUtils.sys.mjs" +); + +add_task(async function test() { + var pageurl = + "http://mochi.test:8888/browser/docshell/test/browser/file_bug420605.html"; + var fragmenturl = + "http://mochi.test:8888/browser/docshell/test/browser/file_bug420605.html#firefox"; + + /* Queries nsINavHistoryService and returns a single history entry + * for a given URI */ + function getNavHistoryEntry(aURI) { + var options = PlacesUtils.history.getNewQueryOptions(); + options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY; + options.maxResults = 1; + + var query = PlacesUtils.history.getNewQuery(); + query.uri = aURI; + var result = PlacesUtils.history.executeQuery(query, options); + result.root.containerOpen = true; + + if (!result.root.childCount) { + return null; + } + return result.root.getChild(0); + } + + // We'll save the favicon URL of the orignal page here and check that the + // page with a hash has the same favicon. + var originalFavicon; + + // Control flow in this test is a bit complicated. + // + // When the page loads, onPageLoad (the DOMContentLoaded handler) and + // favicon-changed are both called, in some order. Once + // they've both run, we click a fragment link in the content page + // (clickLinkIfReady), which should trigger another favicon-changed event, + // this time for the fragment's URL. + + var _clickLinkTimes = 0; + function clickLinkIfReady() { + _clickLinkTimes++; + if (_clickLinkTimes == 2) { + BrowserTestUtils.synthesizeMouseAtCenter( + "#firefox-link", + {}, + gBrowser.selectedBrowser + ); + } + } + + function onPageLoad() { + clickLinkIfReady(); + } + + // Make sure neither of the test pages haven't been loaded before. + var info = getNavHistoryEntry(makeURI(pageurl)); + ok(!info, "The test page must not have been visited already."); + info = getNavHistoryEntry(makeURI(fragmenturl)); + ok(!info, "The fragment test page must not have been visited already."); + + let promiseIcon1 = PlacesTestUtils.waitForNotification( + "favicon-changed", + events => + events.some(e => { + if (e.url == pageurl) { + ok( + e.faviconUrl, + "Favicon value is not null for page without fragment." + ); + originalFavicon = e.faviconUrl; + + // Now that the favicon has loaded, click on fragment link. + // This should trigger the |case fragmenturl| below. + clickLinkIfReady(); + return true; + } + return false; + }), + "places" + ); + let promiseIcon2 = PlacesTestUtils.waitForNotification( + "favicon-changed", + events => + events.some(e => { + if (e.url == fragmenturl) { + // If the fragment URL's favicon isn't set, this branch won't + // be called and the test will time out. + is( + e.faviconUrl, + originalFavicon, + "New favicon should be same as original favicon." + ); + ok( + e.faviconUrl, + "Favicon value is not null for page without fragment." + ); + originalFavicon = e.faviconUrl; + + // Now that the favicon has loaded, click on fragment link. + // This should trigger the |case fragmenturl| below. + clickLinkIfReady(); + return true; + } + return false; + }), + "places" + ); + + // Now open the test page in a new tab. + gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); + BrowserTestUtils.waitForContentEvent( + gBrowser.selectedBrowser, + "DOMContentLoaded", + true + ).then(onPageLoad); + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, pageurl); + + await promiseIcon1; + await promiseIcon2; + + // Let's explicitly check that we can get the favicon + // from nsINavHistoryService now. + info = getNavHistoryEntry(makeURI(fragmenturl)); + ok(info, "There must be a history entry for the fragment."); + ok(info.icon, "The history entry must have an associated favicon."); + gBrowser.removeCurrentTab(); +}); diff --git a/docshell/test/browser/browser_bug422543.js b/docshell/test/browser/browser_bug422543.js new file mode 100644 index 0000000000..886b9c183a --- /dev/null +++ b/docshell/test/browser/browser_bug422543.js @@ -0,0 +1,253 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const ACTOR = "Bug422543"; + +let getActor = browser => { + return browser.browsingContext.currentWindowGlobal.getActor(ACTOR); +}; + +add_task(async function runTests() { + if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) { + await setupAsync(); + let browser = gBrowser.selectedBrowser; + // Now that we're set up, initialize our frame script. + await checkListenersAsync("initial", "listeners initialized"); + + // Check if all history listeners are always notified. + info("# part 1"); + await whenPageShown(browser, () => + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + BrowserTestUtils.loadURI(browser, "http://www.example.com/") + ); + await checkListenersAsync("newentry", "shistory has a new entry"); + ok(browser.canGoBack, "we can go back"); + + await whenPageShown(browser, () => browser.goBack()); + await checkListenersAsync("gotoindex", "back to the first shentry"); + ok(browser.canGoForward, "we can go forward"); + + await whenPageShown(browser, () => browser.goForward()); + await checkListenersAsync("gotoindex", "forward to the second shentry"); + + await whenPageShown(browser, () => browser.reload()); + await checkListenersAsync("reload", "current shentry reloaded"); + + await whenPageShown(browser, () => browser.gotoIndex(0)); + await checkListenersAsync("gotoindex", "back to the first index"); + + // Check nsISHistory.notifyOnHistoryReload + info("# part 2"); + ok(await notifyReloadAsync(), "reloading has not been canceled"); + await checkListenersAsync("reload", "saw the reload notification"); + + // Let the first listener cancel the reload action. + info("# part 3"); + await resetListenersAsync(); + await setListenerRetvalAsync(0, false); + ok(!(await notifyReloadAsync()), "reloading has been canceled"); + await checkListenersAsync("reload", "saw the reload notification"); + + // Let both listeners cancel the reload action. + info("# part 4"); + await resetListenersAsync(); + await setListenerRetvalAsync(1, false); + ok(!(await notifyReloadAsync()), "reloading has been canceled"); + await checkListenersAsync("reload", "saw the reload notification"); + + // Let the second listener cancel the reload action. + info("# part 5"); + await resetListenersAsync(); + await setListenerRetvalAsync(0, true); + ok(!(await notifyReloadAsync()), "reloading has been canceled"); + await checkListenersAsync("reload", "saw the reload notification"); + + function sendQuery(message, arg = {}) { + return getActor(gBrowser.selectedBrowser).sendQuery(message, arg); + } + + function checkListenersAsync(aLast, aMessage) { + return sendQuery("getListenerStatus").then(listenerStatuses => { + is(listenerStatuses[0], aLast, aMessage); + is(listenerStatuses[1], aLast, aMessage); + }); + } + + function resetListenersAsync() { + return sendQuery("resetListeners"); + } + + function notifyReloadAsync() { + return sendQuery("notifyReload").then(({ rval }) => { + return rval; + }); + } + + function setListenerRetvalAsync(num, val) { + return sendQuery("setRetval", { num, val }); + } + + async function setupAsync() { + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "http://mochi.test:8888" + ); + + let base = getRootDirectory(gTestPath).slice(0, -1); + ChromeUtils.registerWindowActor(ACTOR, { + child: { + esModuleURI: `${base}/Bug422543Child.sys.mjs`, + }, + }); + + registerCleanupFunction(async () => { + await sendQuery("cleanup"); + gBrowser.removeTab(tab); + + ChromeUtils.unregisterWindowActor(ACTOR); + }); + + await sendQuery("init"); + } + return; + } + + await setup(); + let browser = gBrowser.selectedBrowser; + // Now that we're set up, initialize our frame script. + checkListeners("initial", "listeners initialized"); + + // Check if all history listeners are always notified. + info("# part 1"); + await whenPageShown(browser, () => + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + BrowserTestUtils.loadURI(browser, "http://www.example.com/") + ); + checkListeners("newentry", "shistory has a new entry"); + ok(browser.canGoBack, "we can go back"); + + await whenPageShown(browser, () => browser.goBack()); + checkListeners("gotoindex", "back to the first shentry"); + ok(browser.canGoForward, "we can go forward"); + + await whenPageShown(browser, () => browser.goForward()); + checkListeners("gotoindex", "forward to the second shentry"); + + await whenPageShown(browser, () => browser.reload()); + checkListeners("reload", "current shentry reloaded"); + + await whenPageShown(browser, () => browser.gotoIndex(0)); + checkListeners("gotoindex", "back to the first index"); + + // Check nsISHistory.notifyOnHistoryReload + info("# part 2"); + ok(notifyReload(browser), "reloading has not been canceled"); + checkListeners("reload", "saw the reload notification"); + + // Let the first listener cancel the reload action. + info("# part 3"); + resetListeners(); + setListenerRetval(0, false); + ok(!notifyReload(browser), "reloading has been canceled"); + checkListeners("reload", "saw the reload notification"); + + // Let both listeners cancel the reload action. + info("# part 4"); + resetListeners(); + setListenerRetval(1, false); + ok(!notifyReload(browser), "reloading has been canceled"); + checkListeners("reload", "saw the reload notification"); + + // Let the second listener cancel the reload action. + info("# part 5"); + resetListeners(); + setListenerRetval(0, true); + ok(!notifyReload(browser), "reloading has been canceled"); + checkListeners("reload", "saw the reload notification"); +}); + +class SHistoryListener { + constructor() { + this.retval = true; + this.last = "initial"; + } + + OnHistoryNewEntry(aNewURI) { + this.last = "newentry"; + } + + OnHistoryGotoIndex() { + this.last = "gotoindex"; + } + + OnHistoryPurge() { + this.last = "purge"; + } + + OnHistoryReload() { + this.last = "reload"; + return this.retval; + } + + OnHistoryReplaceEntry() {} +} +SHistoryListener.prototype.QueryInterface = ChromeUtils.generateQI([ + "nsISHistoryListener", + "nsISupportsWeakReference", +]); + +let listeners = [new SHistoryListener(), new SHistoryListener()]; + +function checkListeners(aLast, aMessage) { + is(listeners[0].last, aLast, aMessage); + is(listeners[1].last, aLast, aMessage); +} + +function resetListeners() { + for (let listener of listeners) { + listener.last = "initial"; + } +} + +function notifyReload(browser) { + return browser.browsingContext.sessionHistory.notifyOnHistoryReload(); +} + +function setListenerRetval(num, val) { + listeners[num].retval = val; +} + +async function setup() { + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "http://mochi.test:8888" + ); + + let browser = tab.linkedBrowser; + registerCleanupFunction(async function() { + for (let listener of listeners) { + browser.browsingContext.sessionHistory.removeSHistoryListener(listener); + } + gBrowser.removeTab(tab); + }); + for (let listener of listeners) { + browser.browsingContext.sessionHistory.addSHistoryListener(listener); + } +} + +function whenPageShown(aBrowser, aNavigation) { + let promise = new Promise(resolve => { + let unregister = BrowserTestUtils.addContentEventListener( + aBrowser, + "pageshow", + () => { + unregister(); + resolve(); + }, + { capture: true } + ); + }); + + aNavigation(); + return promise; +} diff --git a/docshell/test/browser/browser_bug441169.js b/docshell/test/browser/browser_bug441169.js new file mode 100644 index 0000000000..09e83b040c --- /dev/null +++ b/docshell/test/browser/browser_bug441169.js @@ -0,0 +1,44 @@ +/* Make sure that netError won't allow HTML injection through badcert parameters. See bug 441169. */ +var newBrowser; + +function task() { + let resolve; + let promise = new Promise(r => { + resolve = r; + }); + + addEventListener("DOMContentLoaded", checkPage, false); + + function checkPage(event) { + if (event.target != content.document) { + return; + } + removeEventListener("DOMContentLoaded", checkPage, false); + + is( + content.document.getElementById("test_span"), + null, + "Error message should not be parsed as HTML, and hence shouldn't include the 'test_span' element." + ); + resolve(); + } + + var chromeURL = + "about:neterror?e=nssBadCert&u=https%3A//test.kuix.de/&c=UTF-8&d=This%20sentence%20should%20not%20be%20parsed%20to%20include%20a%20%3Cspan%20id=%22test_span%22%3Enamed%3C/span%3E%20span%20tag.%0A%0AThe%20certificate%20is%20only%20valid%20for%20%3Ca%20id=%22cert_domain_link%22%20title=%22kuix.de%22%3Ekuix.de%3C/a%3E%0A%0A(Error%20code%3A%20ssl_error_bad_cert_domain)"; + content.location = chromeURL; + + return promise; +} + +function test() { + waitForExplicitFinish(); + + var newTab = BrowserTestUtils.addTab(gBrowser); + gBrowser.selectedTab = newTab; + newBrowser = gBrowser.getBrowserForTab(newTab); + + ContentTask.spawn(newBrowser, null, task).then(() => { + gBrowser.removeCurrentTab(); + finish(); + }); +} diff --git a/docshell/test/browser/browser_bug503832.js b/docshell/test/browser/browser_bug503832.js new file mode 100644 index 0000000000..c912b83a31 --- /dev/null +++ b/docshell/test/browser/browser_bug503832.js @@ -0,0 +1,76 @@ +/* Test for Bug 503832 + * https://bugzilla.mozilla.org/show_bug.cgi?id=503832 + */ + +add_task(async function() { + var pagetitle = "Page Title for Bug 503832"; + var pageurl = + "http://mochi.test:8888/browser/docshell/test/browser/file_bug503832.html"; + var fragmenturl = + "http://mochi.test:8888/browser/docshell/test/browser/file_bug503832.html#firefox"; + + var historyService = Cc[ + "@mozilla.org/browser/nav-history-service;1" + ].getService(Ci.nsINavHistoryService); + + let fragmentPromise = new Promise(resolve => { + const listener = events => { + const { url, title } = events[0]; + + switch (url) { + case pageurl: + is(title, pagetitle, "Correct page title for " + url); + return; + case fragmenturl: + is(title, pagetitle, "Correct page title for " + url); + // If titles for fragment URLs aren't set, this code + // branch won't be called and the test will timeout, + // resulting in a failure + PlacesObservers.removeListener(["page-title-changed"], listener); + resolve(); + } + }; + + PlacesObservers.addListener(["page-title-changed"], listener); + }); + + /* Queries nsINavHistoryService and returns a single history entry + * for a given URI */ + function getNavHistoryEntry(aURI) { + var options = historyService.getNewQueryOptions(); + options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY; + options.maxResults = 1; + + var query = historyService.getNewQuery(); + query.uri = aURI; + + var result = historyService.executeQuery(query, options); + result.root.containerOpen = true; + + if (!result.root.childCount) { + return null; + } + var node = result.root.getChild(0); + result.root.containerOpen = false; + return node; + } + + // Make sure neither of the test pages haven't been loaded before. + var info = getNavHistoryEntry(makeURI(pageurl)); + ok(!info, "The test page must not have been visited already."); + info = getNavHistoryEntry(makeURI(fragmenturl)); + ok(!info, "The fragment test page must not have been visited already."); + + // Now open the test page in a new tab + await BrowserTestUtils.openNewForegroundTab(gBrowser, pageurl); + + // Now that the page is loaded, click on fragment link + await BrowserTestUtils.synthesizeMouseAtCenter( + "#firefox-link", + {}, + gBrowser.selectedBrowser + ); + await fragmentPromise; + + gBrowser.removeCurrentTab(); +}); diff --git a/docshell/test/browser/browser_bug554155.js b/docshell/test/browser/browser_bug554155.js new file mode 100644 index 0000000000..ad63706195 --- /dev/null +++ b/docshell/test/browser/browser_bug554155.js @@ -0,0 +1,33 @@ +add_task(async function test() { + await BrowserTestUtils.withNewTab( + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + { gBrowser, url: "http://example.com" }, + async function(browser) { + let numLocationChanges = 0; + + let listener = { + onLocationChange(browser, webProgress, request, uri, flags) { + info("location change: " + (uri && uri.spec)); + numLocationChanges++; + }, + }; + + gBrowser.addTabsProgressListener(listener); + + await SpecialPowers.spawn(browser, [], function() { + // pushState to a new URL (http://example.com/foo"). This should trigger + // exactly one LocationChange event. + content.history.pushState(null, null, "foo"); + }); + + await Promise.resolve(); + + gBrowser.removeTabsProgressListener(listener); + is( + numLocationChanges, + 1, + "pushState should cause exactly one LocationChange event." + ); + } + ); +}); diff --git a/docshell/test/browser/browser_bug655270.js b/docshell/test/browser/browser_bug655270.js new file mode 100644 index 0000000000..231105b33e --- /dev/null +++ b/docshell/test/browser/browser_bug655270.js @@ -0,0 +1,64 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test for Bug 655273 + * + * Call pushState and then make sure that the favicon service associates our + * old favicon with the new URI. + */ + +const { PlacesTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PlacesTestUtils.sys.mjs" +); + +add_task(async function test() { + const testDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + const origURL = testDir + "file_bug655270.html"; + const newURL = origURL + "?new_page"; + + const faviconURL = testDir + "favicon_bug655270.ico"; + + let icon1; + let promiseIcon1 = PlacesTestUtils.waitForNotification( + "favicon-changed", + events => + events.some(e => { + if (e.url == origURL) { + icon1 = e.faviconUrl; + return true; + } + return false; + }), + "places" + ); + let icon2; + let promiseIcon2 = PlacesTestUtils.waitForNotification( + "favicon-changed", + events => + events.some(e => { + if (e.url == newURL) { + icon2 = e.faviconUrl; + return true; + } + return false; + }), + "places" + ); + + // The page at origURL has a <link rel='icon'>, so we should get a call into + // our observer below when it loads. Once we verify that we have the right + // favicon URI, we call pushState, which should trigger another favicon change + // event, this time for the URI after pushState. + let tab = BrowserTestUtils.addTab(gBrowser, origURL); + await promiseIcon1; + is(icon1, faviconURL, "FaviconURL for original URI"); + // Ignore the promise returned here and wait for the next + // onPageChanged notification. + SpecialPowers.spawn(tab.linkedBrowser, [], function() { + content.history.pushState("", "", "?new_page"); + }); + await promiseIcon2; + is(icon2, faviconURL, "FaviconURL for new URI"); + gBrowser.removeTab(tab); +}); diff --git a/docshell/test/browser/browser_bug655273.js b/docshell/test/browser/browser_bug655273.js new file mode 100644 index 0000000000..3974d2d86a --- /dev/null +++ b/docshell/test/browser/browser_bug655273.js @@ -0,0 +1,55 @@ +/* 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/. */ + +/** + * Test for Bug 655273. Make sure that after changing the URI via + * history.pushState, the resulting SHEntry has the same title as our old + * SHEntry. + **/ + +add_task(async function test() { + waitForExplicitFinish(); + + await BrowserTestUtils.withNewTab( + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + { gBrowser, url: "http://example.com" }, + async function(browser) { + if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) { + await SpecialPowers.spawn(browser, [], async function() { + let cw = content; + let oldTitle = cw.document.title; + ok(oldTitle, "Content window should initially have a title."); + cw.history.pushState("", "", "new_page"); + + let shistory = cw.docShell.QueryInterface(Ci.nsIWebNavigation) + .sessionHistory; + + is( + shistory.legacySHistory.getEntryAtIndex(shistory.index).title, + oldTitle, + "SHEntry title after pushstate." + ); + }); + + return; + } + + let bc = browser.browsingContext; + let oldTitle = browser.browsingContext.currentWindowGlobal.documentTitle; + ok(oldTitle, "Content window should initially have a title."); + SpecialPowers.spawn(browser, [], async function() { + content.history.pushState("", "", "new_page"); + }); + + let shistory = bc.sessionHistory; + await SHListener.waitForHistory(shistory, SHListener.NewEntry); + + is( + shistory.getEntryAtIndex(shistory.index).title, + oldTitle, + "SHEntry title after pushstate." + ); + } + ); +}); diff --git a/docshell/test/browser/browser_bug670318.js b/docshell/test/browser/browser_bug670318.js new file mode 100644 index 0000000000..cb92ee4159 --- /dev/null +++ b/docshell/test/browser/browser_bug670318.js @@ -0,0 +1,144 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test for Bug 670318 + * + * When LoadEntry() is called on a browser that has multiple duplicate history + * entries, history.index can end up out of range (>= history.count). + */ + +const URL = + "http://mochi.test:8888/browser/docshell/test/browser/file_bug670318.html"; + +add_task(async function test() { + await BrowserTestUtils.withNewTab( + { gBrowser, url: "about:blank" }, + async function(browser) { + if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) { + await ContentTask.spawn(browser, URL, async function(URL) { + let history = docShell.QueryInterface(Ci.nsIWebNavigation) + .sessionHistory; + let count = 0; + + let testDone = {}; + testDone.promise = new Promise(resolve => { + testDone.resolve = resolve; + }); + + // Since listener implements nsISupportsWeakReference, we are + // responsible for keeping it alive so that the GC doesn't clear + // it before the test completes. We do this by anchoring the listener + // to the message manager, and clearing it just before the test + // completes. + this._testListener = { + owner: this, + OnHistoryNewEntry(aNewURI) { + info("OnHistoryNewEntry " + aNewURI.spec + ", " + count); + if (aNewURI.spec == URL && 5 == ++count) { + addEventListener( + "load", + function onLoad() { + Assert.ok( + history.index < history.count, + "history.index is valid" + ); + testDone.resolve(); + }, + { capture: true, once: true } + ); + + history.legacySHistory.removeSHistoryListener( + this.owner._testListener + ); + delete this.owner._testListener; + this.owner = null; + content.setTimeout(() => { + content.location.reload(); + }, 0); + } + }, + + OnHistoryReload: () => true, + OnHistoryGotoIndex: () => {}, + OnHistoryPurge: () => {}, + OnHistoryReplaceEntry: () => { + // The initial load of about:blank causes a transient entry to be + // created, so our first navigation to a real page is a replace + // instead of a new entry. + ++count; + }, + + QueryInterface: ChromeUtils.generateQI([ + "nsISHistoryListener", + "nsISupportsWeakReference", + ]), + }; + + history.legacySHistory.addSHistoryListener(this._testListener); + content.location = URL; + + await testDone.promise; + }); + + return; + } + + let history = browser.browsingContext.sessionHistory; + let count = 0; + + let testDone = {}; + testDone.promise = new Promise(resolve => { + testDone.resolve = resolve; + }); + + let listener = { + async OnHistoryNewEntry(aNewURI) { + if (aNewURI.spec == URL && 5 == ++count) { + history.removeSHistoryListener(listener); + await ContentTask.spawn(browser, null, () => { + return new Promise(resolve => { + addEventListener( + "load", + evt => { + let history = docShell.QueryInterface(Ci.nsIWebNavigation) + .sessionHistory; + Assert.ok( + history.index < history.count, + "history.index is valid" + ); + resolve(); + }, + { capture: true, once: true } + ); + + content.location.reload(); + }); + }); + testDone.resolve(); + } + }, + + OnHistoryReload: () => true, + OnHistoryGotoIndex: () => {}, + OnHistoryPurge: () => {}, + OnHistoryReplaceEntry: () => { + // The initial load of about:blank causes a transient entry to be + // created, so our first navigation to a real page is a replace + // instead of a new entry. + ++count; + }, + + QueryInterface: ChromeUtils.generateQI([ + "nsISHistoryListener", + "nsISupportsWeakReference", + ]), + }; + + history.addSHistoryListener(listener); + BrowserTestUtils.loadURI(browser, URL); + + await testDone.promise; + } + ); +}); diff --git a/docshell/test/browser/browser_bug673087-1.js b/docshell/test/browser/browser_bug673087-1.js new file mode 100644 index 0000000000..427b246d76 --- /dev/null +++ b/docshell/test/browser/browser_bug673087-1.js @@ -0,0 +1,46 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug673087-1.html", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u00A4"), + 151, + "Parent doc should be windows-1252 initially" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u00A4"), + 95, + "Child doc should be windows-1252 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u3042"), + 151, + "Parent doc should decode as EUC-JP subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u3042"), + 95, + "Child doc should decode as EUC-JP subsequently" + ); + + is( + content.document.characterSet, + "EUC-JP", + "Parent doc should report EUC-JP subsequently" + ); + is( + content.frames[0].document.characterSet, + "EUC-JP", + "Child doc should report EUC-JP subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug673087-2.js b/docshell/test/browser/browser_bug673087-2.js new file mode 100644 index 0000000000..13a7a2a82c --- /dev/null +++ b/docshell/test/browser/browser_bug673087-2.js @@ -0,0 +1,36 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug673087-2.html", + afterOpen, + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\uFFFD"), + 0, + "Doc should decode as replacement initially" + ); + + is( + content.document.characterSet, + "replacement", + "Doc should report replacement initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\uFFFD"), + 0, + "Doc should decode as replacement subsequently" + ); + + is( + content.document.characterSet, + "replacement", + "Doc should report replacement subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug673467.js b/docshell/test/browser/browser_bug673467.js new file mode 100644 index 0000000000..182cc0ee80 --- /dev/null +++ b/docshell/test/browser/browser_bug673467.js @@ -0,0 +1,62 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test for bug 673467. In a new tab, load a page which inserts a new iframe +// before the load and then sets its location during the load. This should +// create just one SHEntry. + +var doc = + "data:text/html,<html><body onload='load()'>" + + "<script>" + + " var iframe = document.createElement('iframe');" + + " iframe.id = 'iframe';" + + " document.documentElement.appendChild(iframe);" + + " function load() {" + + " iframe.src = 'data:text/html,Hello!';" + + " }" + + "</script>" + + "</body></html>"; + +function test() { + waitForExplicitFinish(); + + let taskFinished; + + let tab = BrowserTestUtils.addTab(gBrowser, doc, {}, tab => { + taskFinished = ContentTask.spawn(tab.linkedBrowser, null, () => { + return new Promise(resolve => { + addEventListener( + "load", + function() { + // The main page has loaded. Now wait for the iframe to load. + let iframe = content.document.getElementById("iframe"); + iframe.addEventListener( + "load", + function listener(aEvent) { + // Wait for the iframe to load the new document, not about:blank. + if (!iframe.src) { + return; + } + + iframe.removeEventListener("load", listener, true); + let shistory = content.docShell.QueryInterface( + Ci.nsIWebNavigation + ).sessionHistory; + + Assert.equal(shistory.count, 1, "shistory count should be 1."); + resolve(); + }, + true + ); + }, + true + ); + }); + }); + }); + + taskFinished.then(() => { + gBrowser.removeTab(tab); + finish(); + }); +} diff --git a/docshell/test/browser/browser_bug852909.js b/docshell/test/browser/browser_bug852909.js new file mode 100644 index 0000000000..108baa0626 --- /dev/null +++ b/docshell/test/browser/browser_bug852909.js @@ -0,0 +1,35 @@ +var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + +function test() { + waitForExplicitFinish(); + + gBrowser.selectedTab = BrowserTestUtils.addTab( + gBrowser, + rootDir + "file_bug852909.png" + ); + BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(image); +} + +function image(event) { + ok( + !gBrowser.selectedTab.mayEnableCharacterEncodingMenu, + "Docshell should say the menu should be disabled for images." + ); + + gBrowser.removeCurrentTab(); + gBrowser.selectedTab = BrowserTestUtils.addTab( + gBrowser, + rootDir + "file_bug852909.pdf" + ); + BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(pdf); +} + +function pdf(event) { + ok( + !gBrowser.selectedTab.mayEnableCharacterEncodingMenu, + "Docshell should say the menu should be disabled for PDF.js." + ); + + gBrowser.removeCurrentTab(); + finish(); +} diff --git a/docshell/test/browser/browser_bug92473.js b/docshell/test/browser/browser_bug92473.js new file mode 100644 index 0000000000..7e386f5ee9 --- /dev/null +++ b/docshell/test/browser/browser_bug92473.js @@ -0,0 +1,70 @@ +/* The test text as octets for reference + * %83%86%83%6a%83%52%81%5b%83%68%82%cd%81%41%82%b7%82%d7%82%c4%82%cc%95%b6%8e%9a%82%c9%8c%c5%97%4c%82%cc%94%d4%8d%86%82%f0%95%74%97%5e%82%b5%82%dc%82%b7 + */ + +function testContent(text) { + return SpecialPowers.spawn(gBrowser.selectedBrowser, [text], text => { + Assert.equal( + content.document.getElementById("testpar").innerHTML, + text, + "<p> contains expected text" + ); + Assert.equal( + content.document.getElementById("testtextarea").innerHTML, + text, + "<textarea> contains expected text" + ); + Assert.equal( + content.document.getElementById("testinput").value, + text, + "<input> contains expected text" + ); + }); +} + +function afterOpen() { + BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then( + afterChangeCharset + ); + + /* The test text decoded incorrectly as Windows-1251. This is the "right" wrong + text; anything else is unexpected. */ + const wrongText = + "\u0453\u2020\u0453\u006A\u0453\u0052\u0403\u005B\u0453\u0068\u201A\u041D\u0403\u0041\u201A\u00B7\u201A\u0427\u201A\u0414\u201A\u041C\u2022\u00B6\u040B\u0459\u201A\u0419\u040A\u0415\u2014\u004C\u201A\u041C\u201D\u0424\u040C\u2020\u201A\u0440\u2022\u0074\u2014\u005E\u201A\u00B5\u201A\u042C\u201A\u00B7"; + + /* Test that the content on load is the expected wrong decoding */ + testContent(wrongText).then(() => { + BrowserForceEncodingDetection(); + }); +} + +function afterChangeCharset() { + /* The test text decoded correctly as Shift_JIS */ + const rightText = + "\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059"; + + /* test that the content is decoded correctly */ + testContent(rightText).then(() => { + gBrowser.removeCurrentTab(); + finish(); + }); +} + +function test() { + waitForExplicitFinish(); + + // Get the local directory. This needs to be a file: URI because chrome: URIs + // are always UTF-8 (bug 617339) and we are testing decoding from other + // charsets. + var jar = getJar(getRootDirectory(gTestPath)); + var dir = jar + ? extractJarToTmp(jar) + : getChromeDir(getResolvedURI(gTestPath)); + var rootDir = Services.io.newFileURI(dir).spec; + + gBrowser.selectedTab = BrowserTestUtils.addTab( + gBrowser, + rootDir + "test-form_sjis.html" + ); + BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(afterOpen); +} diff --git a/docshell/test/browser/browser_click_link_within_view_source.js b/docshell/test/browser/browser_click_link_within_view_source.js new file mode 100644 index 0000000000..5c298a56c6 --- /dev/null +++ b/docshell/test/browser/browser_click_link_within_view_source.js @@ -0,0 +1,78 @@ +"use strict"; + +/** + * Test for Bug 1359204 + * + * Loading a local file, then view-source on that file. Make sure that + * clicking a link within that view-source page is not blocked by security checks. + */ + +add_task(async function test_click_link_within_view_source() { + let TEST_FILE = "file_click_link_within_view_source.html"; + let TEST_FILE_URI = getChromeDir(getResolvedURI(gTestPath)); + TEST_FILE_URI.append(TEST_FILE); + TEST_FILE_URI = Services.io.newFileURI(TEST_FILE_URI).spec; + + let DUMMY_FILE = "dummy_page.html"; + let DUMMY_FILE_URI = getChromeDir(getResolvedURI(gTestPath)); + DUMMY_FILE_URI.append(DUMMY_FILE); + DUMMY_FILE_URI = Services.io.newFileURI(DUMMY_FILE_URI).spec; + + await BrowserTestUtils.withNewTab(TEST_FILE_URI, async function(aBrowser) { + let tabSpec = gBrowser.selectedBrowser.currentURI.spec; + info("loading: " + tabSpec); + ok( + tabSpec.startsWith("file://") && tabSpec.endsWith(TEST_FILE), + "sanity check to make sure html loaded" + ); + + info("click view-source of html"); + let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser); + document.getElementById("View:PageSource").doCommand(); + + let tab = await tabPromise; + tabSpec = gBrowser.selectedBrowser.currentURI.spec; + info("loading: " + tabSpec); + ok( + tabSpec.startsWith("view-source:file://") && tabSpec.endsWith(TEST_FILE), + "loading view-source of html succeeded" + ); + + info("click testlink within view-source page"); + let loadPromise = BrowserTestUtils.browserLoaded( + tab.linkedBrowser, + false, + url => url.endsWith("dummy_page.html") + ); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function() { + if (content.document.readyState != "complete") { + await ContentTaskUtils.waitForEvent( + content.document, + "readystatechange", + false, + () => content.document.readyState == "complete" + ); + } + // document.getElementById() does not work on a view-source page, hence we use document.links + let linksOnPage = content.document.links; + is( + linksOnPage.length, + 1, + "sanity check: make sure only one link is present on page" + ); + let myLink = content.document.links[0]; + myLink.click(); + }); + + await loadPromise; + + tabSpec = gBrowser.selectedBrowser.currentURI.spec; + info("loading: " + tabSpec); + ok( + tabSpec.startsWith("view-source:file://") && tabSpec.endsWith(DUMMY_FILE), + "loading view-source of html succeeded" + ); + + BrowserTestUtils.removeTab(tab); + }); +}); diff --git a/docshell/test/browser/browser_cross_process_csp_inheritance.js b/docshell/test/browser/browser_cross_process_csp_inheritance.js new file mode 100644 index 0000000000..504700e6c4 --- /dev/null +++ b/docshell/test/browser/browser_cross_process_csp_inheritance.js @@ -0,0 +1,125 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com" +); +const TEST_URI = TEST_PATH + "file_cross_process_csp_inheritance.html"; +const DATA_URI = + "data:text/html,<html>test-same-diff-process-csp-inhertiance</html>"; + +const FISSION_ENABLED = SpecialPowers.useRemoteSubframes; + +function getCurrentPID(aBrowser) { + return SpecialPowers.spawn(aBrowser, [], () => { + return Services.appinfo.processID; + }); +} + +function getCurrentURI(aBrowser) { + return SpecialPowers.spawn(aBrowser, [], () => { + let channel = content.docShell.currentDocumentChannel; + return channel.URI.asciiSpec; + }); +} + +function verifyResult( + aTestName, + aBrowser, + aDataURI, + aPID, + aSamePID, + aFissionEnabled +) { + return SpecialPowers.spawn( + aBrowser, + [{ aTestName, aDataURI, aPID, aSamePID, aFissionEnabled }], + async function({ aTestName, aDataURI, aPID, aSamePID, aFissionEnabled }) { + // sanity, to make sure the correct URI was loaded + let channel = content.docShell.currentDocumentChannel; + is( + channel.URI.asciiSpec, + aDataURI, + aTestName + ": correct data uri loaded" + ); + + // check that the process ID is the same/different when opening the new tab + let pid = Services.appinfo.processID; + if (aSamePID) { + is(pid, aPID, aTestName + ": process ID needs to be identical"); + } else if (aFissionEnabled) { + // TODO: Fission discards dom.noopener.newprocess.enabled and puts + // data: URIs in the same process. Unfortunately todo_isnot is not + // defined in that scope, hence we have to use a workaround. + todo( + false, + pid == aPID, + ": process ID needs to be different in fission" + ); + } else { + isnot(pid, aPID, aTestName + ": process ID needs to be different"); + } + + // finally, evaluate that the CSP was set. + let cspOBJ = JSON.parse(content.document.cspJSON); + let policies = cspOBJ["csp-policies"]; + is(policies.length, 1, "should be one policy"); + let policy = policies[0]; + is( + policy["script-src"], + "'none'", + aTestName + ": script-src directive matches" + ); + } + ); +} + +async function simulateCspInheritanceForNewTab(aTestName, aSamePID) { + await BrowserTestUtils.withNewTab(TEST_URI, async function(browser) { + // do some sanity checks + let currentURI = await getCurrentURI(gBrowser.selectedBrowser); + is(currentURI, TEST_URI, aTestName + ": correct test uri loaded"); + + let pid = await getCurrentPID(gBrowser.selectedBrowser); + let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, DATA_URI, true); + // simulate click + BrowserTestUtils.synthesizeMouseAtCenter( + "#testLink", + {}, + gBrowser.selectedBrowser + ); + let tab = await loadPromise; + gBrowser.selectTabAtIndex(2); + await verifyResult( + aTestName, + gBrowser.selectedBrowser, + DATA_URI, + pid, + aSamePID, + FISSION_ENABLED + ); + await BrowserTestUtils.removeTab(tab); + }); +} + +add_task(async function test_csp_inheritance_diff_process() { + // forcing the new data: URI load to happen in a *new* process by flipping the pref + // to force <a rel="noopener" ...> to be loaded in a new process. + await SpecialPowers.pushPrefEnv({ + set: [["dom.noopener.newprocess.enabled", true]], + }); + await simulateCspInheritanceForNewTab("diff-process-inheritance", false); +}); + +add_task(async function test_csp_inheritance_same_process() { + // forcing the new data: URI load to happen in a *same* process by resetting the pref + // and loaded <a rel="noopener" ...> in the *same* process. + await SpecialPowers.pushPrefEnv({ + set: [["dom.noopener.newprocess.enabled", false]], + }); + await simulateCspInheritanceForNewTab("same-process-inheritance", true); +}); diff --git a/docshell/test/browser/browser_csp_sandbox_no_script_js_uri.js b/docshell/test/browser/browser_csp_sandbox_no_script_js_uri.js new file mode 100644 index 0000000000..d0b92084ec --- /dev/null +++ b/docshell/test/browser/browser_csp_sandbox_no_script_js_uri.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); + +/** + * Test that javascript URIs in CSP-sandboxed contexts can't be used to bypass + * script restrictions. + */ +add_task(async function test_csp_sandbox_no_script_js_uri() { + await BrowserTestUtils.withNewTab( + TEST_PATH + "dummy_page.html", + async browser => { + info("Register observer and wait for javascript-uri-blocked message."); + let observerPromise = SpecialPowers.spawn(browser, [], () => { + return new Promise(resolve => { + SpecialPowers.addObserver(function obs(subject) { + ok( + subject == content, + "Should block script spawned via javascript uri" + ); + SpecialPowers.removeObserver( + obs, + "javascript-uri-blocked-by-sandbox" + ); + resolve(); + }, "javascript-uri-blocked-by-sandbox"); + }); + }); + + info("Spawn csp-sandboxed iframe with javascript URI"); + let frameBC = await SpecialPowers.spawn( + browser, + [TEST_PATH + "file_csp_sandbox_no_script_js_uri.html"], + async url => { + let frame = content.document.createElement("iframe"); + let loadPromise = ContentTaskUtils.waitForEvent(frame, "load", true); + frame.src = url; + content.document.body.appendChild(frame); + await loadPromise; + return frame.browsingContext; + } + ); + + info("Click javascript URI link in iframe"); + BrowserTestUtils.synthesizeMouseAtCenter("a", {}, frameBC); + await observerPromise; + } + ); +}); diff --git a/docshell/test/browser/browser_csp_uir.js b/docshell/test/browser/browser_csp_uir.js new file mode 100644 index 0000000000..9eb5ec1a89 --- /dev/null +++ b/docshell/test/browser/browser_csp_uir.js @@ -0,0 +1,89 @@ +"use strict"; + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com" +); +const TEST_URI = TEST_PATH + "file_csp_uir.html"; // important to be http: to test upgrade-insecure-requests +const RESULT_URI = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + TEST_PATH.replace("http://", "https://") + "file_csp_uir_dummy.html"; + +function verifyCSP(aTestName, aBrowser, aResultURI) { + return SpecialPowers.spawn( + aBrowser, + [{ aTestName, aResultURI }], + async function({ aTestName, aResultURI }) { + let channel = content.docShell.currentDocumentChannel; + is(channel.URI.asciiSpec, aResultURI, "testing CSP for " + aTestName); + } + ); +} + +add_task(async function test_csp_inheritance_regular_click() { + await BrowserTestUtils.withNewTab(TEST_URI, async function(browser) { + let loadPromise = BrowserTestUtils.browserLoaded( + browser, + false, + RESULT_URI + ); + // set the data href + simulate click + BrowserTestUtils.synthesizeMouseAtCenter( + "#testlink", + {}, + gBrowser.selectedBrowser + ); + await loadPromise; + await verifyCSP("click()", gBrowser.selectedBrowser, RESULT_URI); + }); +}); + +add_task(async function test_csp_inheritance_ctrl_click() { + await BrowserTestUtils.withNewTab(TEST_URI, async function(browser) { + let loadPromise = BrowserTestUtils.waitForNewTab( + gBrowser, + RESULT_URI, + true + ); + // set the data href + simulate ctrl+click + BrowserTestUtils.synthesizeMouseAtCenter( + "#testlink", + { ctrlKey: true, metaKey: true }, + gBrowser.selectedBrowser + ); + let tab = await loadPromise; + gBrowser.selectTabAtIndex(2); + await verifyCSP("ctrl-click()", gBrowser.selectedBrowser, RESULT_URI); + await BrowserTestUtils.removeTab(tab); + }); +}); + +add_task( + async function test_csp_inheritance_right_click_open_link_in_new_tab() { + await BrowserTestUtils.withNewTab(TEST_URI, async function(browser) { + let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, RESULT_URI); + // set the data href + simulate right-click open link in tab + BrowserTestUtils.waitForEvent(document, "popupshown", false, event => { + // These are operations that must be executed synchronously with the event. + document.getElementById("context-openlinkintab").doCommand(); + event.target.hidePopup(); + return true; + }); + BrowserTestUtils.synthesizeMouseAtCenter( + "#testlink", + { type: "contextmenu", button: 2 }, + gBrowser.selectedBrowser + ); + + let tab = await loadPromise; + gBrowser.selectTabAtIndex(2); + await verifyCSP( + "right-click-open-in-new-tab()", + gBrowser.selectedBrowser, + RESULT_URI + ); + await BrowserTestUtils.removeTab(tab); + }); + } +); diff --git a/docshell/test/browser/browser_dataURI_unique_opaque_origin.js b/docshell/test/browser/browser_dataURI_unique_opaque_origin.js new file mode 100644 index 0000000000..76a8eb4251 --- /dev/null +++ b/docshell/test/browser/browser_dataURI_unique_opaque_origin.js @@ -0,0 +1,30 @@ +add_task(async function test_dataURI_unique_opaque_origin() { + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + let tab = BrowserTestUtils.addTab(gBrowser, "http://example.com"); + let browser = tab.linkedBrowser; + await BrowserTestUtils.browserLoaded(browser); + + let pagePrincipal = browser.contentPrincipal; + info("pagePrincial " + pagePrincipal.origin); + + BrowserTestUtils.loadURI(browser, "data:text/html,hi"); + await BrowserTestUtils.browserLoaded(browser); + + await SpecialPowers.spawn( + browser, + [{ principal: pagePrincipal }], + async function(args) { + info("data URI principal: " + content.document.nodePrincipal.origin); + Assert.ok( + content.document.nodePrincipal.isNullPrincipal, + "data: URI should have NullPrincipal." + ); + Assert.ok( + !content.document.nodePrincipal.equals(args.principal), + "data: URI should have unique opaque origin." + ); + } + ); + + gBrowser.removeTab(tab); +}); diff --git a/docshell/test/browser/browser_data_load_inherit_csp.js b/docshell/test/browser/browser_data_load_inherit_csp.js new file mode 100644 index 0000000000..8ad05ef7e3 --- /dev/null +++ b/docshell/test/browser/browser_data_load_inherit_csp.js @@ -0,0 +1,110 @@ +"use strict"; + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com" +); +const HTML_URI = TEST_PATH + "file_data_load_inherit_csp.html"; +const DATA_URI = "data:text/html;html,<html><body>foo</body></html>"; + +function setDataHrefOnLink(aBrowser, aDataURI) { + return SpecialPowers.spawn(aBrowser, [aDataURI], function(uri) { + let link = content.document.getElementById("testlink"); + link.href = uri; + }); +} + +function verifyCSP(aTestName, aBrowser, aDataURI) { + return SpecialPowers.spawn( + aBrowser, + [{ aTestName, aDataURI }], + async function({ aTestName, aDataURI }) { + let channel = content.docShell.currentDocumentChannel; + is(channel.URI.spec, aDataURI, "testing CSP for " + aTestName); + let cspJSON = content.document.cspJSON; + let cspOBJ = JSON.parse(cspJSON); + let policies = cspOBJ["csp-policies"]; + is(policies.length, 1, "should be one policy"); + let policy = policies[0]; + is( + policy["script-src"], + "'unsafe-inline'", + "script-src directive matches" + ); + } + ); +} + +add_setup(async function() { + // allow top level data: URI navigations, otherwise clicking data: link fails + await SpecialPowers.pushPrefEnv({ + set: [["security.data_uri.block_toplevel_data_uri_navigations", false]], + }); +}); + +add_task(async function test_data_csp_inheritance_regular_click() { + await BrowserTestUtils.withNewTab(HTML_URI, async function(browser) { + let loadPromise = BrowserTestUtils.browserLoaded(browser, false, DATA_URI); + // set the data href + simulate click + await setDataHrefOnLink(gBrowser.selectedBrowser, DATA_URI); + BrowserTestUtils.synthesizeMouseAtCenter( + "#testlink", + {}, + gBrowser.selectedBrowser + ); + await loadPromise; + await verifyCSP("click()", gBrowser.selectedBrowser, DATA_URI); + }); +}); + +add_task(async function test_data_csp_inheritance_ctrl_click() { + await BrowserTestUtils.withNewTab(HTML_URI, async function(browser) { + let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, DATA_URI, true); + // set the data href + simulate ctrl+click + await setDataHrefOnLink(gBrowser.selectedBrowser, DATA_URI); + BrowserTestUtils.synthesizeMouseAtCenter( + "#testlink", + { ctrlKey: true, metaKey: true }, + gBrowser.selectedBrowser + ); + let tab = await loadPromise; + gBrowser.selectTabAtIndex(2); + await verifyCSP("ctrl-click()", gBrowser.selectedBrowser, DATA_URI); + await BrowserTestUtils.removeTab(tab); + }); +}); + +add_task( + async function test_data_csp_inheritance_right_click_open_link_in_new_tab() { + await BrowserTestUtils.withNewTab(HTML_URI, async function(browser) { + let loadPromise = BrowserTestUtils.waitForNewTab( + gBrowser, + DATA_URI, + true + ); + // set the data href + simulate right-click open link in tab + await setDataHrefOnLink(gBrowser.selectedBrowser, DATA_URI); + BrowserTestUtils.waitForEvent(document, "popupshown", false, event => { + // These are operations that must be executed synchronously with the event. + document.getElementById("context-openlinkintab").doCommand(); + event.target.hidePopup(); + return true; + }); + BrowserTestUtils.synthesizeMouseAtCenter( + "#testlink", + { type: "contextmenu", button: 2 }, + gBrowser.selectedBrowser + ); + + let tab = await loadPromise; + gBrowser.selectTabAtIndex(2); + await verifyCSP( + "right-click-open-in-new-tab()", + gBrowser.selectedBrowser, + DATA_URI + ); + await BrowserTestUtils.removeTab(tab); + }); + } +); diff --git a/docshell/test/browser/browser_fall_back_to_https.js b/docshell/test/browser/browser_fall_back_to_https.js new file mode 100644 index 0000000000..b84780eec1 --- /dev/null +++ b/docshell/test/browser/browser_fall_back_to_https.js @@ -0,0 +1,73 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* + * This test is for bug 1002724. + * https://bugzilla.mozilla.org/show_bug.cgi?id=1002724 + * + * When a user enters a host name or IP address in the URL bar, "http" is + * assumed. If the host rejects connections on port 80, we try HTTPS as a + * fall-back and only fail if HTTPS connection fails. + * + * This tests that when a user enters "example.com", it attempts to load + * http://example.com:80 (not rejected), and when trying secureonly.example.com + * (which rejects connections on port 80), it fails then loads + * https://secureonly.example.com:443 instead. + */ + +const { UrlbarTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/UrlbarTestUtils.sys.mjs" +); + +const bug1002724_tests = [ + { + original: "example.com", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + expected: "http://example.com", + explanation: "Should load HTTP version of example.com", + }, + { + original: "secureonly.example.com", + expected: "https://secureonly.example.com", + explanation: + "Should reject secureonly.example.com on HTTP but load the HTTPS version", + }, +]; + +async function test_one(test_obj) { + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:blank" + ); + gURLBar.focus(); + gURLBar.value = test_obj.original; + + let loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false); + EventUtils.synthesizeKey("KEY_Enter"); + await loadPromise; + + ok( + tab.linkedBrowser.currentURI.spec.startsWith(test_obj.expected), + test_obj.explanation + ); + + BrowserTestUtils.removeTab(tab); +} + +add_task(async function test_bug1002724() { + await SpecialPowers.pushPrefEnv( + // Disable HSTS preload just in case. + { + set: [ + ["network.stricttransportsecurity.preloadlist", false], + ["network.dns.native-is-localhost", true], + ], + } + ); + + for (let test of bug1002724_tests) { + await test_one(test); + } +}); diff --git a/docshell/test/browser/browser_fission_maxOrigins.js b/docshell/test/browser/browser_fission_maxOrigins.js new file mode 100644 index 0000000000..b8efbe0ca1 --- /dev/null +++ b/docshell/test/browser/browser_fission_maxOrigins.js @@ -0,0 +1,222 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +SimpleTest.requestFlakyTimeout("Need to test expiration timeout"); + +function delay(msec) { + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + return new Promise(resolve => setTimeout(resolve, msec)); +} + +function promiseIdle() { + return new Promise(resolve => { + Services.tm.idleDispatchToMainThread(resolve); + }); +} + +const ORIGIN_CAP = 5; +const SLIDING_WINDOW_MS = 5000; + +const PREF_ORIGIN_CAP = "fission.experiment.max-origins.origin-cap"; +const PREF_SLIDING_WINDOW_MS = + "fission.experiment.max-origins.sliding-window-ms"; +const PREF_QUALIFIED = "fission.experiment.max-origins.qualified"; +const PREF_LAST_QUALIFIED = "fission.experiment.max-origins.last-qualified"; +const PREF_LAST_DISQUALIFIED = + "fission.experiment.max-origins.last-disqualified"; + +const SITE_ORIGINS = [ + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.net/", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.tw/", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.cn/", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.fi/", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.in/", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.lk/", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://w3c-test.org/", + "https://www.mozilla.org/", +]; + +function openTab(url) { + return BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url, + waitForStateStop: true, + }); +} + +async function assertQualified() { + // The unique origin calculation runs from an idle task, so make sure + // the queued idle task has had a chance to run. + await promiseIdle(); + + // Make sure the clock has advanced since the qualification timestamp + // was recorded. + await delay(1); + + let qualified = Services.prefs.getBoolPref(PREF_QUALIFIED); + let lastQualified = Services.prefs.getIntPref(PREF_LAST_QUALIFIED); + let lastDisqualified = Services.prefs.getIntPref(PREF_LAST_DISQUALIFIED); + let currentTime = Date.now() / 1000; + + ok(qualified, "Should be qualified"); + ok( + lastQualified > 0, + `Last qualified timestamp (${lastQualified}) should be greater than 0` + ); + ok( + lastQualified < currentTime, + `Last qualified timestamp (${lastQualified}) should be less than the current time (${currentTime})` + ); + ok( + lastQualified > lastDisqualified, + `Last qualified timestamp (${lastQualified}) should be after the last disqualified time (${lastDisqualified})` + ); + + ok( + lastDisqualified < currentTime, + `Last disqualified timestamp (${lastDisqualified}) should be less than the current time (${currentTime})` + ); +} + +async function assertDisqualified(opts) { + // The unique origin calculation runs from an idle task, so make sure + // the queued idle task has had a chance to run. + await promiseIdle(); + + let qualified = Services.prefs.getBoolPref(PREF_QUALIFIED); + let lastQualified = Services.prefs.getIntPref(PREF_LAST_QUALIFIED, 0); + let lastDisqualified = Services.prefs.getIntPref(PREF_LAST_DISQUALIFIED); + let currentTime = Date.now() / 1000; + + ok(!qualified, "Should not be qualified"); + if (!opts.initialValues) { + ok( + lastQualified > 0, + `Last qualified timestamp (${lastQualified}) should be greater than 0` + ); + } + ok( + lastQualified < currentTime, + `Last qualified timestamp (${lastQualified}) should be less than the current time (${currentTime})` + ); + + ok( + lastDisqualified < currentTime, + `Last disqualified timestamp (${lastDisqualified}) should be less than the current time (${currentTime})` + ); + + ok( + lastDisqualified > 0, + `Last disqualified timestamp (${lastDisqualified}) should be greater than 0` + ); + + if (opts.qualificationPending) { + ok( + lastQualified > lastDisqualified, + `Last qualified timestamp (${lastQualified}) should be after the last disqualified time (${lastDisqualified})` + ); + } else { + ok( + lastDisqualified > lastQualified, + `Last disqualified timestamp (${lastDisqualified}) should be after the last qualified time (${lastQualified})` + ); + } +} + +add_task(async function() { + await SpecialPowers.pushPrefEnv({ + set: [ + [PREF_ORIGIN_CAP, ORIGIN_CAP], + [PREF_SLIDING_WINDOW_MS, SLIDING_WINDOW_MS], + ], + }); + + const { BrowserTelemetryUtils } = ChromeUtils.importESModule( + "resource://gre/modules/BrowserTelemetryUtils.sys.mjs" + ); + + // Make sure we actually record telemetry for our disqualifying origin + // count. + BrowserTelemetryUtils.min_interval = 1; + + let tabs = []; + + // Open one initial tab to make sure the origin counting code has had + // a chance to run before checking the initial state. + tabs.push(await openTab("http://mochi.test:8888/")); + + await assertQualified(); + + let lastDisqualified = Services.prefs.getIntPref(PREF_LAST_DISQUALIFIED); + is(lastDisqualified, 0, "Last disqualification timestamp should be 0"); + + info( + `Opening ${SITE_ORIGINS.length} tabs with distinct origins to exceed the cap (${ORIGIN_CAP})` + ); + ok( + SITE_ORIGINS.length > ORIGIN_CAP, + "Should have enough site origins to exceed the origin cap" + ); + tabs.push(...(await Promise.all(SITE_ORIGINS.map(openTab)))); + + await assertDisqualified({ qualificationPending: false }); + + info("Close unique-origin tabs"); + await Promise.all(tabs.map(tab => BrowserTestUtils.removeTab(tab))); + + info("Open a new tab to trigger the origin count code once more"); + tabs = [await openTab(SITE_ORIGINS[0])]; + + await assertDisqualified({ qualificationPending: true }); + + info( + "Wait long enough to clear the sliding window since last disqualified state" + ); + await delay(SLIDING_WINDOW_MS + 1000); + + info("Open a new tab to trigger the origin count code again"); + tabs.push(await openTab(SITE_ORIGINS[0])); + + await assertQualified(); + + info( + "Clear preference values and re-populate the initial value from telemetry" + ); + Services.prefs.clearUserPref(PREF_QUALIFIED); + Services.prefs.clearUserPref(PREF_LAST_QUALIFIED); + Services.prefs.clearUserPref(PREF_LAST_DISQUALIFIED); + BrowserTelemetryUtils._checkedInitialExperimentQualification = false; + + info("Open a new tab to trigger the origin count code again"); + tabs.push(await openTab(SITE_ORIGINS[0])); + + await assertDisqualified({ initialValues: true }); + + info( + "Wait long enough to clear the sliding window since last disqualified state" + ); + await delay(SLIDING_WINDOW_MS + 1000); + + info("Open a new tab to trigger the origin count code again"); + tabs.push(await openTab(SITE_ORIGINS[0])); + + await assertQualified(); + + await Promise.all(tabs.map(tab => BrowserTestUtils.removeTab(tab))); + + // Clear the cached recording interval so it resets to the default + // value on the next call. + BrowserTelemetryUtils.min_interval = null; +}); diff --git a/docshell/test/browser/browser_frameloader_swap_with_bfcache.js b/docshell/test/browser/browser_frameloader_swap_with_bfcache.js new file mode 100644 index 0000000000..eba49064f1 --- /dev/null +++ b/docshell/test/browser/browser_frameloader_swap_with_bfcache.js @@ -0,0 +1,37 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; +add_task(async function test() { + if ( + !SpecialPowers.Services.appinfo.sessionHistoryInParent || + !SpecialPowers.Services.prefs.getBoolPref("fission.bfcacheInParent") + ) { + ok( + true, + "This test is currently only for the bfcache in the parent process." + ); + return; + } + const uri = + "data:text/html,<script>onpageshow = function(e) { document.documentElement.setAttribute('persisted', e.persisted); }; </script>"; + let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, uri, true); + let browser1 = tab1.linkedBrowser; + + let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, uri, true); + let browser2 = tab2.linkedBrowser; + BrowserTestUtils.loadURI(browser2, uri + "nextpage"); + await BrowserTestUtils.browserLoaded(browser2, false); + + gBrowser.swapBrowsersAndCloseOther(tab1, tab2); + is(tab1.linkedBrowser, browser1, "Tab's browser should stay the same."); + browser1.goBack(false); + await BrowserTestUtils.browserLoaded(browser1, false); + let persisted = await SpecialPowers.spawn(browser1, [], async function() { + return content.document.documentElement.getAttribute("persisted"); + }); + + is(persisted, "false", "BFCache should be evicted when swapping browsers."); + + BrowserTestUtils.removeTab(tab1); +}); diff --git a/docshell/test/browser/browser_history_triggeringprincipal_viewsource.js b/docshell/test/browser/browser_history_triggeringprincipal_viewsource.js new file mode 100644 index 0000000000..808232460c --- /dev/null +++ b/docshell/test/browser/browser_history_triggeringprincipal_viewsource.js @@ -0,0 +1,88 @@ +"use strict"; + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com" +); +const HTML_URI = TEST_PATH + "dummy_page.html"; +const VIEW_SRC_URI = "view-source:" + HTML_URI; + +add_task(async function() { + await SpecialPowers.pushPrefEnv({ + set: [["browser.navigation.requireUserInteraction", false]], + }); + + info("load baseline html in new tab"); + await BrowserTestUtils.withNewTab(HTML_URI, async function(aBrowser) { + is( + gBrowser.selectedBrowser.currentURI.spec, + HTML_URI, + "sanity check to make sure html loaded" + ); + + info("right-click -> view-source of html"); + let vSrcCtxtMenu = document.getElementById("contentAreaContextMenu"); + let popupPromise = BrowserTestUtils.waitForEvent( + vSrcCtxtMenu, + "popupshown" + ); + BrowserTestUtils.synthesizeMouseAtCenter( + "body", + { type: "contextmenu", button: 2 }, + aBrowser + ); + await popupPromise; + let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, VIEW_SRC_URI); + let vSrcItem = vSrcCtxtMenu.querySelector("#context-viewsource"); + vSrcCtxtMenu.activateItem(vSrcItem); + let tab = await tabPromise; + is( + gBrowser.selectedBrowser.currentURI.spec, + VIEW_SRC_URI, + "loading view-source of html succeeded" + ); + + info("load html file again before going .back()"); + let loadPromise = BrowserTestUtils.browserLoaded( + tab.linkedBrowser, + false, + HTML_URI + ); + await SpecialPowers.spawn(tab.linkedBrowser, [HTML_URI], HTML_URI => { + content.document.location = HTML_URI; + }); + await loadPromise; + is( + gBrowser.selectedBrowser.currentURI.spec, + HTML_URI, + "loading html another time succeeded" + ); + + info( + "click .back() to view-source of html again and make sure the history entry has a triggeringPrincipal" + ); + let backCtxtMenu = document.getElementById("contentAreaContextMenu"); + popupPromise = BrowserTestUtils.waitForEvent(backCtxtMenu, "popupshown"); + BrowserTestUtils.synthesizeMouseAtCenter( + "body", + { type: "contextmenu", button: 2 }, + aBrowser + ); + await popupPromise; + loadPromise = BrowserTestUtils.waitForContentEvent( + tab.linkedBrowser, + "pageshow" + ); + let backItem = backCtxtMenu.querySelector("#context-back"); + backCtxtMenu.activateItem(backItem); + await loadPromise; + is( + gBrowser.selectedBrowser.currentURI.spec, + VIEW_SRC_URI, + "clicking .back() to view-source of html succeeded" + ); + + BrowserTestUtils.removeTab(tab); + }); +}); diff --git a/docshell/test/browser/browser_isInitialDocument.js b/docshell/test/browser/browser_isInitialDocument.js new file mode 100644 index 0000000000..7b62be8f6f --- /dev/null +++ b/docshell/test/browser/browser_isInitialDocument.js @@ -0,0 +1,323 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tag every new WindowGlobalParent with an expando indicating whether or not +// they were an initial document when they were created for the duration of this +// test. +function wasInitialDocumentObserver(subject) { + subject._test_wasInitialDocument = subject.isInitialDocument; +} +Services.obs.addObserver(wasInitialDocumentObserver, "window-global-created"); +SimpleTest.registerCleanupFunction(function() { + Services.obs.removeObserver( + wasInitialDocumentObserver, + "window-global-created" + ); +}); + +add_task(async function new_about_blank_tab() { + await BrowserTestUtils.withNewTab("about:blank", async browser => { + is( + browser.browsingContext.currentWindowGlobal.isInitialDocument, + false, + "After loading an actual, final about:blank in the tab, the field is false" + ); + }); +}); + +add_task(async function iframe_initial_about_blank() { + await BrowserTestUtils.withNewTab( + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/document-builder.sjs?html=com", + async browser => { + info("Create an iframe without any explicit location"); + await SpecialPowers.spawn(browser, [], async () => { + const iframe = content.document.createElement("iframe"); + // Add the iframe to the DOM tree in order to be able to have its browsingContext + content.document.body.appendChild(iframe); + const { browsingContext } = iframe; + + is( + iframe.contentDocument.isInitialDocument, + true, + "The field is true on just-created iframes" + ); + let beforeLoadPromise = SpecialPowers.spawnChrome( + [browsingContext], + bc => [ + bc.currentWindowGlobal.isInitialDocument, + bc.currentWindowGlobal._test_wasInitialDocument, + ] + ); + + await new Promise(resolve => { + iframe.addEventListener("load", resolve, { once: true }); + }); + is( + iframe.contentDocument.isInitialDocument, + false, + "The field is false after having loaded the final about:blank document" + ); + let afterLoadPromise = SpecialPowers.spawnChrome( + [browsingContext], + bc => [ + bc.currentWindowGlobal.isInitialDocument, + bc.currentWindowGlobal._test_wasInitialDocument, + ] + ); + + // Wait to await the parent process promises, so we can't miss the "load" event. + let [beforeIsInitial, beforeWasInitial] = await beforeLoadPromise; + is(beforeIsInitial, true, "before load is initial in parent"); + is(beforeWasInitial, true, "before load was initial in parent"); + let [afterIsInitial, afterWasInitial] = await afterLoadPromise; + is(afterIsInitial, false, "after load is not initial in parent"); + is(afterWasInitial, true, "after load was initial in parent"); + iframe.remove(); + }); + + info("Create an iframe with a cross origin location"); + const iframeBC = await SpecialPowers.spawn(browser, [], async () => { + const iframe = content.document.createElement("iframe"); + await new Promise(resolve => { + iframe.addEventListener("load", resolve, { once: true }); + iframe.src = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/document-builder.sjs?html=org-iframe"; + content.document.body.appendChild(iframe); + }); + + return iframe.browsingContext; + }); + + is( + iframeBC.currentWindowGlobal.isInitialDocument, + false, + "The field is true after having loaded the final document" + ); + } + ); +}); + +add_task(async function window_open() { + async function testWindowOpen({ browser, args, isCrossOrigin, willLoad }) { + info(`Open popup with ${JSON.stringify(args)}`); + const onNewTab = BrowserTestUtils.waitForNewTab( + gBrowser, + args[0] || "about:blank" + ); + await SpecialPowers.spawn( + browser, + [args, isCrossOrigin, willLoad], + async (args, crossOrigin, willLoad) => { + const win = content.window.open(...args); + is( + win.document.isInitialDocument, + true, + "The field is true right after calling window.open()" + ); + let beforeLoadPromise = SpecialPowers.spawnChrome( + [win.browsingContext], + bc => [ + bc.currentWindowGlobal.isInitialDocument, + bc.currentWindowGlobal._test_wasInitialDocument, + ] + ); + + // In cross origin, it is harder to watch for new document load, and if + // no argument is passed no load will happen. + if (!crossOrigin && willLoad) { + await new Promise(r => + win.addEventListener("load", r, { once: true }) + ); + is( + win.document.isInitialDocument, + false, + "The field becomes false right after the popup document is loaded" + ); + } + + // Perform the await after the load to avoid missing it. + let [beforeIsInitial, beforeWasInitial] = await beforeLoadPromise; + is(beforeIsInitial, true, "before load is initial in parent"); + is(beforeWasInitial, true, "before load was initial in parent"); + } + ); + const newTab = await onNewTab; + const windowGlobal = + newTab.linkedBrowser.browsingContext.currentWindowGlobal; + if (willLoad) { + is( + windowGlobal.isInitialDocument, + false, + "The field is false in the parent process after having loaded the final document" + ); + } else { + is( + windowGlobal.isInitialDocument, + true, + "The field remains true in the parent process as nothing will be loaded" + ); + } + BrowserTestUtils.removeTab(newTab); + } + + await BrowserTestUtils.withNewTab( + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/document-builder.sjs?html=com", + async browser => { + info("Use window.open() with cross-origin document"); + await testWindowOpen({ + browser, + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + args: ["http://example.org/document-builder.sjs?html=org-popup"], + isCrossOrigin: true, + willLoad: true, + }); + + info("Use window.open() with same-origin document"); + await testWindowOpen({ + browser, + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + args: ["http://example.com/document-builder.sjs?html=com-popup"], + isCrossOrigin: false, + willLoad: true, + }); + + info("Use window.open() with final about:blank document"); + await testWindowOpen({ + browser, + args: ["about:blank"], + isCrossOrigin: false, + willLoad: true, + }); + + info("Use window.open() with no argument"); + await testWindowOpen({ + browser, + args: [], + isCrossOrigin: false, + willLoad: false, + }); + } + ); +}); + +add_task(async function document_open() { + await BrowserTestUtils.withNewTab( + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/document-builder.sjs?html=com", + async browser => { + is(browser.browsingContext.currentWindowGlobal.isInitialDocument, false); + await SpecialPowers.spawn(browser, [], async () => { + const iframe = content.document.createElement("iframe"); + // Add the iframe to the DOM tree in order to be able to have its browsingContext + content.document.body.appendChild(iframe); + const { browsingContext } = iframe; + + // Check the state before the call in both parent and content. + is( + iframe.contentDocument.isInitialDocument, + true, + "Is an initial document before calling document.open" + ); + let beforeOpenParentPromise = SpecialPowers.spawnChrome( + [browsingContext], + bc => [ + bc.currentWindowGlobal.isInitialDocument, + bc.currentWindowGlobal._test_wasInitialDocument, + bc.currentWindowGlobal.innerWindowId, + ] + ); + + // Run the `document.open` call with reduced permissions. + iframe.contentWindow.eval(` + document.open(); + document.write("new document"); + document.close(); + `); + + is( + iframe.contentDocument.isInitialDocument, + false, + "Is no longer an initial document after calling document.open" + ); + let [ + afterIsInitial, + afterWasInitial, + afterID, + ] = await SpecialPowers.spawnChrome([browsingContext], bc => [ + bc.currentWindowGlobal.isInitialDocument, + bc.currentWindowGlobal._test_wasInitialDocument, + bc.currentWindowGlobal.innerWindowId, + ]); + let [ + beforeIsInitial, + beforeWasInitial, + beforeID, + ] = await beforeOpenParentPromise; + is(beforeIsInitial, true, "Should be initial before in the parent"); + is(beforeWasInitial, true, "Was initial before in the parent"); + is(afterIsInitial, false, "Should not be initial after in the parent"); + is(afterWasInitial, true, "Was initial after in the parent"); + is(beforeID, afterID, "Should be the same WindowGlobalParent"); + }); + } + ); +}); + +add_task(async function windowless_browser() { + info("Create a Windowless browser"); + const browser = Services.appShell.createWindowlessBrowser(false); + const { browsingContext } = browser; + is( + browsingContext.currentWindowGlobal.isInitialDocument, + true, + "The field is true for a freshly created WindowlessBrowser" + ); + is( + browser.currentURI.spec, + "about:blank", + "The location is immediately set to about:blank" + ); + + const principal = Services.scriptSecurityManager.getSystemPrincipal(); + browser.docShell.createAboutBlankContentViewer(principal, principal); + is( + browsingContext.currentWindowGlobal.isInitialDocument, + false, + "The field becomes false when creating an artificial blank document" + ); + + info("Load a final about:blank document in it"); + const onLocationChange = new Promise(resolve => { + let wpl = { + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), + onLocationChange() { + browsingContext.webProgress.removeProgressListener( + wpl, + Ci.nsIWebProgress.NOTIFY_ALL + ); + resolve(); + }, + }; + browsingContext.webProgress.addProgressListener( + wpl, + Ci.nsIWebProgress.NOTIFY_ALL + ); + }); + browser.loadURI("about:blank", { triggeringPrincipal: principal }); + info("Wait for the location change"); + await onLocationChange; + is( + browsingContext.currentWindowGlobal.isInitialDocument, + false, + "The field is false after the location change event" + ); + browser.close(); +}); diff --git a/docshell/test/browser/browser_loadURI_postdata.js b/docshell/test/browser/browser_loadURI_postdata.js new file mode 100644 index 0000000000..616fbd9d8e --- /dev/null +++ b/docshell/test/browser/browser_loadURI_postdata.js @@ -0,0 +1,42 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const gPostData = "postdata=true"; +const gUrl = + "http://mochi.test:8888/browser/docshell/test/browser/print_postdata.sjs"; + +add_task(async function test_loadURI_persists_postData() { + waitForExplicitFinish(); + + let tab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser)); + registerCleanupFunction(function() { + gBrowser.removeTab(tab); + }); + + var dataStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( + Ci.nsIStringInputStream + ); + dataStream.data = gPostData; + + var postStream = Cc[ + "@mozilla.org/network/mime-input-stream;1" + ].createInstance(Ci.nsIMIMEInputStream); + postStream.addHeader("Content-Type", "application/x-www-form-urlencoded"); + postStream.setData(dataStream); + var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"].getService( + Ci.nsIPrincipal + ); + + tab.linkedBrowser.loadURI(gUrl, { + triggeringPrincipal: systemPrincipal, + postData: postStream, + }); + await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, gUrl); + let body = await SpecialPowers.spawn( + tab.linkedBrowser, + [], + () => content.document.body.textContent + ); + is(body, gPostData, "post data was submitted correctly"); + finish(); +}); diff --git a/docshell/test/browser/browser_multiple_pushState.js b/docshell/test/browser/browser_multiple_pushState.js new file mode 100644 index 0000000000..5917841c0f --- /dev/null +++ b/docshell/test/browser/browser_multiple_pushState.js @@ -0,0 +1,25 @@ +add_task(async function test_multiple_pushState() { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/docshell/test/browser/file_multiple_pushState.html", + }, + async function(browser) { + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + const kExpected = "http://example.org/bar/ABC/DEF?key=baz"; + + let contentLocation = await SpecialPowers.spawn( + browser, + [], + async function() { + return content.document.location.href; + } + ); + + is(contentLocation, kExpected); + is(browser.documentURI.spec, kExpected); + } + ); +}); diff --git a/docshell/test/browser/browser_onbeforeunload_frame.js b/docshell/test/browser/browser_onbeforeunload_frame.js new file mode 100644 index 0000000000..266407864d --- /dev/null +++ b/docshell/test/browser/browser_onbeforeunload_frame.js @@ -0,0 +1,45 @@ +"use strict"; + +// We need to test a lot of permutations here, and there isn't any sensible way +// to split them up or run them faster. +requestLongerTimeout(12); + +Services.scriptloader.loadSubScript( + getRootDirectory(gTestPath) + "head_browser_onbeforeunload.js", + this +); + +add_task(async function() { + await SpecialPowers.pushPrefEnv({ + set: [["dom.require_user_interaction_for_beforeunload", false]], + }); + + for (let actions of PERMUTATIONS) { + info( + `Testing frame actions: [${actions.map(action => + ACTION_NAMES.get(action) + )}]` + ); + + for (let startIdx = 0; startIdx < FRAMES.length; startIdx++) { + info(`Testing content reload from frame ${startIdx}`); + + await doTest(actions, startIdx, (tab, frames) => { + return SpecialPowers.spawn(frames[startIdx], [], () => { + let eventLoopSpun = false; + SpecialPowers.Services.tm.dispatchToMainThread(() => { + eventLoopSpun = true; + }); + + content.location.reload(); + + return { eventLoopSpun }; + }); + }); + } + } +}); + +add_task(async function cleanup() { + await TabPool.cleanup(); +}); diff --git a/docshell/test/browser/browser_onbeforeunload_navigation.js b/docshell/test/browser/browser_onbeforeunload_navigation.js new file mode 100644 index 0000000000..df1d8c93d4 --- /dev/null +++ b/docshell/test/browser/browser_onbeforeunload_navigation.js @@ -0,0 +1,165 @@ +"use strict"; + +const TEST_PAGE = + "http://mochi.test:8888/browser/docshell/test/browser/file_bug1046022.html"; +const TARGETED_PAGE = + "data:text/html," + + encodeURIComponent("<body>Shouldn't be seeing this</body>"); + +const { PromptTestUtils } = ChromeUtils.import( + "resource://testing-common/PromptTestUtils.jsm" +); + +var loadStarted = false; +var tabStateListener = { + resolveLoad: null, + expectLoad: null, + + onStateChange(webprogress, request, flags, status) { + const WPL = Ci.nsIWebProgressListener; + if (flags & WPL.STATE_IS_WINDOW) { + if (flags & WPL.STATE_START) { + loadStarted = true; + } else if (flags & WPL.STATE_STOP) { + let url = request.QueryInterface(Ci.nsIChannel).URI.spec; + is(url, this.expectLoad, "Should only see expected document loads"); + if (url == this.expectLoad) { + this.resolveLoad(); + } + } + } + }, + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), +}; + +function promiseLoaded(url, callback) { + if (tabStateListener.expectLoad) { + throw new Error("Can't wait for multiple loads at once"); + } + tabStateListener.expectLoad = url; + return new Promise(resolve => { + tabStateListener.resolveLoad = resolve; + if (callback) { + callback(); + } + }).then(() => { + tabStateListener.expectLoad = null; + tabStateListener.resolveLoad = null; + }); +} + +function promiseStayOnPagePrompt(browser, acceptNavigation) { + return PromptTestUtils.handleNextPrompt( + browser, + { modalType: Services.prompt.MODAL_TYPE_CONTENT, promptType: "confirmEx" }, + { buttonNumClick: acceptNavigation ? 0 : 1 } + ); +} + +add_task(async function test() { + await SpecialPowers.pushPrefEnv({ + set: [["dom.require_user_interaction_for_beforeunload", false]], + }); + + let testTab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_PAGE, + false, + true + ); + let browser = testTab.linkedBrowser; + browser.addProgressListener( + tabStateListener, + Ci.nsIWebProgress.NOTIFY_STATE_WINDOW + ); + + const NUM_TESTS = 7; + await SpecialPowers.spawn(browser, [NUM_TESTS], testCount => { + let { testFns } = this.content.wrappedJSObject; + Assert.equal( + testFns.length, + testCount, + "Should have the correct number of test functions" + ); + }); + + for (let allowNavigation of [false, true]) { + for (let i = 0; i < NUM_TESTS; i++) { + info( + `Running test ${i} with navigation ${ + allowNavigation ? "allowed" : "forbidden" + }` + ); + + if (allowNavigation) { + // If we're allowing navigations, we need to re-load the test + // page after each test, since the tests will each navigate away + // from it. + await promiseLoaded(TEST_PAGE, () => { + browser.loadURI(TEST_PAGE, { + triggeringPrincipal: document.nodePrincipal, + }); + }); + } + + let promptPromise = promiseStayOnPagePrompt(browser, allowNavigation); + let loadPromise; + if (allowNavigation) { + loadPromise = promiseLoaded(TARGETED_PAGE); + } + + let winID = await SpecialPowers.spawn( + browser, + [i, TARGETED_PAGE], + (testIdx, url) => { + let { testFns } = this.content.wrappedJSObject; + this.content.onbeforeunload = testFns[testIdx]; + this.content.location = url; + return this.content.windowGlobalChild.innerWindowId; + } + ); + + await promptPromise; + await loadPromise; + + if (allowNavigation) { + await SpecialPowers.spawn( + browser, + [TARGETED_PAGE, winID], + (url, winID) => { + this.content.onbeforeunload = null; + Assert.equal( + this.content.location.href, + url, + "Page should have navigated to the correct URL" + ); + Assert.notEqual( + this.content.windowGlobalChild.innerWindowId, + winID, + "Page should have a new inner window" + ); + } + ); + } else { + await SpecialPowers.spawn(browser, [TEST_PAGE, winID], (url, winID) => { + this.content.onbeforeunload = null; + Assert.equal( + this.content.location.href, + url, + "Page should have the same URL" + ); + Assert.equal( + this.content.windowGlobalChild.innerWindowId, + winID, + "Page should have the same inner window" + ); + }); + } + } + } + + gBrowser.removeTab(testTab); +}); diff --git a/docshell/test/browser/browser_onbeforeunload_parent.js b/docshell/test/browser/browser_onbeforeunload_parent.js new file mode 100644 index 0000000000..79cf815734 --- /dev/null +++ b/docshell/test/browser/browser_onbeforeunload_parent.js @@ -0,0 +1,48 @@ +"use strict"; + +// We need to test a lot of permutations here, and there isn't any sensible way +// to split them up or run them faster. +requestLongerTimeout(6); + +Services.scriptloader.loadSubScript( + getRootDirectory(gTestPath) + "head_browser_onbeforeunload.js", + this +); + +add_task(async function() { + await SpecialPowers.pushPrefEnv({ + set: [["dom.require_user_interaction_for_beforeunload", false]], + }); + + for (let actions of PERMUTATIONS) { + info( + `Testing frame actions: [${actions.map(action => + ACTION_NAMES.get(action) + )}]` + ); + + info(`Testing tab close from parent process`); + await doTest(actions, -1, (tab, frames) => { + let eventLoopSpun = false; + Services.tm.dispatchToMainThread(() => { + eventLoopSpun = true; + }); + + BrowserTestUtils.removeTab(tab); + + let result = { eventLoopSpun }; + + // Make an extra couple of trips through the event loop to give us time + // to process SpecialPowers.spawn responses before resolving. + return new Promise(resolve => { + executeSoon(() => { + executeSoon(() => resolve(result)); + }); + }); + }); + } +}); + +add_task(async function cleanup() { + await TabPool.cleanup(); +}); diff --git a/docshell/test/browser/browser_onunload_stop.js b/docshell/test/browser/browser_onunload_stop.js new file mode 100644 index 0000000000..fe3c0c0834 --- /dev/null +++ b/docshell/test/browser/browser_onunload_stop.js @@ -0,0 +1,23 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_PAGE_1 = + "http://mochi.test:8888/browser/docshell/test/browser/dummy_page.html"; + +const TEST_PAGE_2 = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/browser/docshell/test/browser/dummy_page.html"; + +add_task(async function test() { + await BrowserTestUtils.withNewTab(TEST_PAGE_1, async function(browser) { + let loaded = BrowserTestUtils.browserLoaded(browser, false, TEST_PAGE_2); + await SpecialPowers.spawn(browser, [], () => { + content.addEventListener("unload", e => e.currentTarget.stop(), true); + }); + BrowserTestUtils.loadURI(browser, TEST_PAGE_2); + await loaded; + ok(true, "Page loaded successfully"); + }); +}); diff --git a/docshell/test/browser/browser_overlink.js b/docshell/test/browser/browser_overlink.js new file mode 100644 index 0000000000..64973985ad --- /dev/null +++ b/docshell/test/browser/browser_overlink.js @@ -0,0 +1,27 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); + +add_task(async function test_stripAuthCredentials() { + await BrowserTestUtils.withNewTab( + TEST_PATH + "overlink_test.html", + async function(browser) { + await SpecialPowers.spawn(browser, [], function() { + content.document.getElementById("link").focus(); + }); + + await TestUtils.waitForCondition( + () => XULBrowserWindow.overLink == "https://example.com", + "Overlink should be missing auth credentials" + ); + + ok(true, "Test successful"); + } + ); +}); diff --git a/docshell/test/browser/browser_platform_emulation.js b/docshell/test/browser/browser_platform_emulation.js new file mode 100644 index 0000000000..3a9d3abe94 --- /dev/null +++ b/docshell/test/browser/browser_platform_emulation.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const URL = "data:text/html;charset=utf-8,<iframe id='test-iframe'></iframe>"; + +async function contentTaskNoOverride() { + let docshell = docShell; + is( + docshell.browsingContext.customPlatform, + "", + "There should initially be no customPlatform" + ); +} + +async function contentTaskOverride() { + let docshell = docShell; + is( + docshell.browsingContext.customPlatform, + "foo", + "The platform should be changed to foo" + ); + + is( + content.navigator.platform, + "foo", + "The platform should be changed to foo" + ); + + let frameWin = content.document.querySelector("#test-iframe").contentWindow; + is( + frameWin.navigator.platform, + "foo", + "The platform should be passed on to frames." + ); + + let newFrame = content.document.createElement("iframe"); + content.document.body.appendChild(newFrame); + + let newFrameWin = newFrame.contentWindow; + is( + newFrameWin.navigator.platform, + "foo", + "Newly created frames should use the new platform" + ); + + newFrameWin.location.reload(); + await ContentTaskUtils.waitForEvent(newFrame, "load"); + + is( + newFrameWin.navigator.platform, + "foo", + "New platform should persist across reloads" + ); +} + +add_task(async function() { + await BrowserTestUtils.withNewTab({ gBrowser, url: URL }, async function( + browser + ) { + await SpecialPowers.spawn(browser, [], contentTaskNoOverride); + + let browsingContext = BrowserTestUtils.getBrowsingContextFrom(browser); + browsingContext.customPlatform = "foo"; + + await SpecialPowers.spawn(browser, [], contentTaskOverride); + }); +}); diff --git a/docshell/test/browser/browser_search_notification.js b/docshell/test/browser/browser_search_notification.js new file mode 100644 index 0000000000..291b670862 --- /dev/null +++ b/docshell/test/browser/browser_search_notification.js @@ -0,0 +1,49 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { SearchTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/SearchTestUtils.sys.mjs" +); + +SearchTestUtils.init(this); + +add_task(async function() { + // Our search would be handled by the urlbar normally and not by the docshell, + // thus we must force going through dns first, so that the urlbar thinks + // the value may be a url, and asks the docshell to visit it. + // On NS_ERROR_UNKNOWN_HOST the docshell will fix it up. + await SpecialPowers.pushPrefEnv({ + set: [["browser.fixup.dns_first_for_single_words", true]], + }); + const kSearchEngineID = "test_urifixup_search_engine"; + await SearchTestUtils.installSearchExtension( + { + name: kSearchEngineID, + search_url: "http://localhost/", + search_url_get_params: "search={searchTerms}", + }, + { setAsDefault: true } + ); + + let selectedName = (await Services.search.getDefault()).name; + Assert.equal( + selectedName, + kSearchEngineID, + "Check fake search engine is selected" + ); + + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); + gBrowser.selectedTab = tab; + + gURLBar.value = "firefox"; + gURLBar.handleCommand(); + + let [subject, data] = await TestUtils.topicObserved("keyword-search"); + + let engine = subject.QueryInterface(Ci.nsISupportsString).data; + + Assert.equal(engine, kSearchEngineID, "Should be the search engine id"); + Assert.equal(data, "firefox", "Notification data is search term."); + + gBrowser.removeTab(tab); +}); diff --git a/docshell/test/browser/browser_tab_replace_while_loading.js b/docshell/test/browser/browser_tab_replace_while_loading.js new file mode 100644 index 0000000000..e1b88334ff --- /dev/null +++ b/docshell/test/browser/browser_tab_replace_while_loading.js @@ -0,0 +1,83 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* Test for bug 1578379. */ + +add_task(async function test_window_open_about_blank() { + const URL = + "http://mochi.test:8888/browser/docshell/test/browser/file_open_about_blank.html"; + let firstTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL); + let promiseTabOpened = BrowserTestUtils.waitForNewTab( + gBrowser, + "about:blank" + ); + + info("Opening about:blank using a click"); + await SpecialPowers.spawn(firstTab.linkedBrowser, [""], async function() { + content.document.querySelector("#open").click(); + }); + + info("Waiting for the second tab to be opened"); + let secondTab = await promiseTabOpened; + + info("Detaching tab"); + let windowOpenedPromise = BrowserTestUtils.waitForNewWindow(); + gBrowser.replaceTabWithWindow(secondTab); + let win = await windowOpenedPromise; + + info("Asserting document is visible"); + let tab = win.gBrowser.selectedTab; + await SpecialPowers.spawn(tab.linkedBrowser, [""], async function() { + is( + content.document.visibilityState, + "visible", + "Document should be visible" + ); + }); + + await BrowserTestUtils.closeWindow(win); + await BrowserTestUtils.removeTab(firstTab); +}); + +add_task(async function test_detach_loading_page() { + const URL = + "http://mochi.test:8888/browser/docshell/test/browser/file_slow_load.sjs"; + // Open a dummy tab so that detaching the second tab works. + let dummyTab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:blank" + ); + let slowLoadingTab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + URL, + /* waitForLoad = */ false + ); + + info("Wait for content document to be created"); + await BrowserTestUtils.waitForCondition(async function() { + return SpecialPowers.spawn( + slowLoadingTab.linkedBrowser, + [URL], + async function(url) { + return content.document.documentURI == url; + } + ); + }); + + info("Detaching tab"); + let windowOpenedPromise = BrowserTestUtils.waitForNewWindow(); + gBrowser.replaceTabWithWindow(slowLoadingTab); + let win = await windowOpenedPromise; + + info("Asserting document is visible"); + let tab = win.gBrowser.selectedTab; + await SpecialPowers.spawn(tab.linkedBrowser, [""], async function() { + is(content.document.readyState, "loading"); + is(content.document.visibilityState, "visible"); + }); + + await BrowserTestUtils.closeWindow(win); + await BrowserTestUtils.removeTab(dummyTab); +}); diff --git a/docshell/test/browser/browser_tab_touch_events.js b/docshell/test/browser/browser_tab_touch_events.js new file mode 100644 index 0000000000..8e66e12253 --- /dev/null +++ b/docshell/test/browser/browser_tab_touch_events.js @@ -0,0 +1,74 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function() { + const URI = "data:text/html;charset=utf-8,<iframe id='test-iframe'></iframe>"; + + await BrowserTestUtils.withNewTab({ gBrowser, url: URI }, async function( + browser + ) { + await SpecialPowers.spawn(browser, [], test_init); + + browser.browsingContext.touchEventsOverride = "disabled"; + + await SpecialPowers.spawn(browser, [], test_body); + }); +}); + +async function test_init() { + is( + content.browsingContext.touchEventsOverride, + "none", + "touchEventsOverride flag should be initially set to NONE" + ); +} + +async function test_body() { + let bc = content.browsingContext; + is( + bc.touchEventsOverride, + "disabled", + "touchEventsOverride flag should be changed to DISABLED" + ); + + let frameWin = content.document.querySelector("#test-iframe").contentWindow; + bc = frameWin.browsingContext; + is( + bc.touchEventsOverride, + "disabled", + "touchEventsOverride flag should be passed on to frames." + ); + + let newFrame = content.document.createElement("iframe"); + content.document.body.appendChild(newFrame); + + let newFrameWin = newFrame.contentWindow; + bc = newFrameWin.browsingContext; + is( + bc.touchEventsOverride, + "disabled", + "Newly created frames should use the new touchEventsOverride flag" + ); + + // Wait for the non-transient about:blank to load. + await ContentTaskUtils.waitForEvent(newFrame, "load"); + newFrameWin = newFrame.contentWindow; + bc = newFrameWin.browsingContext; + is( + bc.touchEventsOverride, + "disabled", + "Newly created frames should use the new touchEventsOverride flag" + ); + + newFrameWin.location.reload(); + await ContentTaskUtils.waitForEvent(newFrame, "load"); + newFrameWin = newFrame.contentWindow; + bc = newFrameWin.browsingContext; + is( + bc.touchEventsOverride, + "disabled", + "New touchEventsOverride flag should persist across reloads" + ); +} diff --git a/docshell/test/browser/browser_targetTopLevelLinkClicksToBlank.js b/docshell/test/browser/browser_targetTopLevelLinkClicksToBlank.js new file mode 100644 index 0000000000..41cbb0f82c --- /dev/null +++ b/docshell/test/browser/browser_targetTopLevelLinkClicksToBlank.js @@ -0,0 +1,285 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * This test exercises the behaviour where user-initiated link clicks on + * the top-level document result in pageloads in a _blank target in a new + * browser window. + */ + +const TEST_PAGE = "https://example.com/browser/"; +const TEST_PAGE_2 = "https://example.com/browser/components/"; +const TEST_IFRAME_PAGE = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" + ) + "dummy_iframe_page.html"; + +// There is an <a> element with this href=".." in the TEST_PAGE +// that we will click, which should take us up a level. +const LINK_URL = "https://example.com/"; + +/** + * Test that a user-initiated link click results in targeting to a new + * <browser> element, and that this properly sets the referrer on the newly + * loaded document. + */ +add_task(async function target_to_new_blank_browser() { + let win = await BrowserTestUtils.openNewBrowserWindow(); + let originalTab = win.gBrowser.selectedTab; + let originalBrowser = originalTab.linkedBrowser; + BrowserTestUtils.loadURI(originalBrowser, TEST_PAGE); + await BrowserTestUtils.browserLoaded(originalBrowser, false, TEST_PAGE); + + // Now set the targetTopLevelLinkClicksToBlank property to true, since it + // defaults to false. + originalBrowser.browsingContext.targetTopLevelLinkClicksToBlank = true; + + let newTabPromise = BrowserTestUtils.waitForNewTab(win.gBrowser, LINK_URL); + await SpecialPowers.spawn(originalBrowser, [], async () => { + let anchor = content.document.querySelector(`a[href=".."]`); + let userInput = content.windowUtils.setHandlingUserInput(true); + try { + anchor.click(); + } finally { + userInput.destruct(); + } + }); + let newTab = await newTabPromise; + let newBrowser = newTab.linkedBrowser; + + Assert.ok( + originalBrowser !== newBrowser, + "A new browser should have been created." + ); + await SpecialPowers.spawn(newBrowser, [TEST_PAGE], async referrer => { + Assert.equal( + content.document.referrer, + referrer, + "Should have gotten the right referrer set" + ); + }); + await BrowserTestUtils.switchTab(win.gBrowser, originalTab); + BrowserTestUtils.removeTab(newTab); + + // Now do the same thing with a subframe targeting "_top". This should also + // get redirected to "_blank". + BrowserTestUtils.loadURI(originalBrowser, TEST_IFRAME_PAGE); + await BrowserTestUtils.browserLoaded( + originalBrowser, + false, + TEST_IFRAME_PAGE + ); + + newTabPromise = BrowserTestUtils.waitForNewTab(win.gBrowser, LINK_URL); + let frameBC1 = originalBrowser.browsingContext.children[0]; + Assert.ok(frameBC1, "Should have found a subframe BrowsingContext"); + + await SpecialPowers.spawn(frameBC1, [LINK_URL], async linkUrl => { + let anchor = content.document.createElement("a"); + anchor.setAttribute("href", linkUrl); + anchor.setAttribute("target", "_top"); + content.document.body.appendChild(anchor); + let userInput = content.windowUtils.setHandlingUserInput(true); + try { + anchor.click(); + } finally { + userInput.destruct(); + } + }); + newTab = await newTabPromise; + newBrowser = newTab.linkedBrowser; + + Assert.ok( + originalBrowser !== newBrowser, + "A new browser should have been created." + ); + await SpecialPowers.spawn( + newBrowser, + [frameBC1.currentURI.spec], + async referrer => { + Assert.equal( + content.document.referrer, + referrer, + "Should have gotten the right referrer set" + ); + } + ); + await BrowserTestUtils.switchTab(win.gBrowser, originalTab); + BrowserTestUtils.removeTab(newTab); + + await BrowserTestUtils.closeWindow(win); +}); + +/** + * Test that we don't target to _blank loads caused by: + * 1. POST requests + * 2. Any load that isn't "normal" (in the nsIDocShell.LOAD_CMD_NORMAL sense) + * 3. Any loads that are caused by location.replace + * 4. Any loads that were caused by setting location.href + * 5. Link clicks fired without user interaction. + */ +add_task(async function skip_blank_target_for_some_loads() { + let win = await BrowserTestUtils.openNewBrowserWindow(); + let currentBrowser = win.gBrowser.selectedBrowser; + BrowserTestUtils.loadURI(currentBrowser, TEST_PAGE); + await BrowserTestUtils.browserLoaded(currentBrowser, false, TEST_PAGE); + + // Now set the targetTopLevelLinkClicksToBlank property to true, since it + // defaults to false. + currentBrowser.browsingContext.targetTopLevelLinkClicksToBlank = true; + + let ensureSingleBrowser = () => { + Assert.equal( + win.gBrowser.browsers.length, + 1, + "There should only be 1 browser." + ); + + Assert.ok( + currentBrowser.browsingContext.targetTopLevelLinkClicksToBlank, + "Should still be targeting top-level clicks to _blank" + ); + }; + + // First we'll test a POST request + let sameBrowserLoad = BrowserTestUtils.browserLoaded( + currentBrowser, + false, + TEST_PAGE + ); + await SpecialPowers.spawn(currentBrowser, [], async () => { + let doc = content.document; + let form = doc.createElement("form"); + form.setAttribute("method", "post"); + doc.body.appendChild(form); + let userInput = content.windowUtils.setHandlingUserInput(true); + try { + form.submit(); + } finally { + userInput.destruct(); + } + }); + await sameBrowserLoad; + ensureSingleBrowser(); + + // Next, we'll try a non-normal load - specifically, we'll try a reload. + // Since we've got a page loaded via a POST request, an attempt to reload + // will cause the "repost" dialog to appear, so we temporarily allow the + // repost to go through with the always_accept testing pref. + await SpecialPowers.pushPrefEnv({ + set: [["dom.confirm_repost.testing.always_accept", true]], + }); + sameBrowserLoad = BrowserTestUtils.browserLoaded( + currentBrowser, + false, + TEST_PAGE + ); + await SpecialPowers.spawn(currentBrowser, [], async () => { + let userInput = content.windowUtils.setHandlingUserInput(true); + try { + content.location.reload(); + } finally { + userInput.destruct(); + } + }); + await sameBrowserLoad; + ensureSingleBrowser(); + await SpecialPowers.popPrefEnv(); + + // Next, we'll try a location.replace + sameBrowserLoad = BrowserTestUtils.browserLoaded( + currentBrowser, + false, + TEST_PAGE_2 + ); + await SpecialPowers.spawn(currentBrowser, [TEST_PAGE_2], async page2 => { + let userInput = content.windowUtils.setHandlingUserInput(true); + try { + content.location.replace(page2); + } finally { + userInput.destruct(); + } + }); + await sameBrowserLoad; + ensureSingleBrowser(); + + // Finally we'll try setting location.href + sameBrowserLoad = BrowserTestUtils.browserLoaded( + currentBrowser, + false, + TEST_PAGE + ); + await SpecialPowers.spawn(currentBrowser, [TEST_PAGE], async page1 => { + let userInput = content.windowUtils.setHandlingUserInput(true); + try { + content.location.href = page1; + } finally { + userInput.destruct(); + } + }); + await sameBrowserLoad; + ensureSingleBrowser(); + + // Now that we're back at TEST_PAGE, let's try a scripted link click. This + // shouldn't target to _blank. + sameBrowserLoad = BrowserTestUtils.browserLoaded( + currentBrowser, + false, + LINK_URL + ); + await SpecialPowers.spawn(currentBrowser, [], async () => { + let anchor = content.document.querySelector(`a[href=".."]`); + anchor.click(); + }); + await sameBrowserLoad; + ensureSingleBrowser(); + + // A javascript:void(0); link should also not target to _blank. + sameBrowserLoad = BrowserTestUtils.browserLoaded( + currentBrowser, + false, + TEST_PAGE + ); + await SpecialPowers.spawn(currentBrowser, [TEST_PAGE], async newPageURL => { + let anchor = content.document.querySelector(`a[href=".."]`); + anchor.href = "javascript:void(0);"; + anchor.addEventListener("click", e => { + content.location.href = newPageURL; + }); + let userInput = content.windowUtils.setHandlingUserInput(true); + try { + anchor.click(); + } finally { + userInput.destruct(); + } + }); + await sameBrowserLoad; + ensureSingleBrowser(); + + // Let's also try a non-void javascript: location. + sameBrowserLoad = BrowserTestUtils.browserLoaded( + currentBrowser, + false, + TEST_PAGE + ); + await SpecialPowers.spawn(currentBrowser, [TEST_PAGE], async newPageURL => { + let anchor = content.document.querySelector(`a[href=".."]`); + anchor.href = `javascript:"string-to-navigate-to"`; + anchor.addEventListener("click", e => { + content.location.href = newPageURL; + }); + let userInput = content.windowUtils.setHandlingUserInput(true); + try { + anchor.click(); + } finally { + userInput.destruct(); + } + }); + await sameBrowserLoad; + ensureSingleBrowser(); + + await BrowserTestUtils.closeWindow(win); +}); diff --git a/docshell/test/browser/browser_timelineMarkers-01.js b/docshell/test/browser/browser_timelineMarkers-01.js new file mode 100644 index 0000000000..3109b6d427 --- /dev/null +++ b/docshell/test/browser/browser_timelineMarkers-01.js @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that the docShell has the right profile timeline API + +const URL = "data:text/html;charset=utf-8,Test page"; + +add_task(async function() { + await BrowserTestUtils.withNewTab({ gBrowser, url: URL }, async function( + browser + ) { + await SpecialPowers.spawn(browser, [], function() { + ok( + "recordProfileTimelineMarkers" in docShell, + "The recordProfileTimelineMarkers attribute exists" + ); + ok( + "popProfileTimelineMarkers" in docShell, + "The popProfileTimelineMarkers function exists" + ); + ok( + docShell.recordProfileTimelineMarkers === false, + "recordProfileTimelineMarkers is false by default" + ); + ok( + docShell.popProfileTimelineMarkers().length === 0, + "There are no markers by default" + ); + + docShell.recordProfileTimelineMarkers = true; + ok( + docShell.recordProfileTimelineMarkers === true, + "recordProfileTimelineMarkers can be set to true" + ); + + docShell.recordProfileTimelineMarkers = false; + ok( + docShell.recordProfileTimelineMarkers === false, + "recordProfileTimelineMarkers can be set to false" + ); + }); + }); +}); diff --git a/docshell/test/browser/browser_timelineMarkers-02.js b/docshell/test/browser/browser_timelineMarkers-02.js new file mode 100644 index 0000000000..a2b569d9d6 --- /dev/null +++ b/docshell/test/browser/browser_timelineMarkers-02.js @@ -0,0 +1,16 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +var TEST_URL = + "<!DOCTYPE html><style>" + + "body {margin:0; padding: 0;} " + + "div {width:100px;height:100px;background:red;} " + + ".resize-change-color {width:50px;height:50px;background:blue;} " + + ".change-color {width:50px;height:50px;background:yellow;} " + + ".add-class {}" + + "</style><div></div>"; +TEST_URL = "data:text/html;charset=utf8," + encodeURIComponent(TEST_URL); + +var test = makeTimelineTest("browser_timelineMarkers-frame-02.js", TEST_URL); diff --git a/docshell/test/browser/browser_timelineMarkers-03.js b/docshell/test/browser/browser_timelineMarkers-03.js new file mode 100644 index 0000000000..b104367c10 --- /dev/null +++ b/docshell/test/browser/browser_timelineMarkers-03.js @@ -0,0 +1,8 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +var URL = "data:text/html;charset=utf-8,<p>Test page</p>"; + +var test = makeTimelineTest("browser_timelineMarkers-frame-03.js", URL); diff --git a/docshell/test/browser/browser_timelineMarkers-04.js b/docshell/test/browser/browser_timelineMarkers-04.js new file mode 100644 index 0000000000..3630b0683f --- /dev/null +++ b/docshell/test/browser/browser_timelineMarkers-04.js @@ -0,0 +1,9 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const URL = + "http://mochi.test:8888/browser/docshell/test/browser/timelineMarkers-04.html"; + +var test = makeTimelineTest("browser_timelineMarkers-frame-04.js", URL); diff --git a/docshell/test/browser/browser_timelineMarkers-05.js b/docshell/test/browser/browser_timelineMarkers-05.js new file mode 100644 index 0000000000..391ce54a92 --- /dev/null +++ b/docshell/test/browser/browser_timelineMarkers-05.js @@ -0,0 +1,16 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +var TEST_URL = + "<!DOCTYPE html><style>" + + "body {margin:0; padding: 0;} " + + "div {width:100px;height:100px;background:red;} " + + ".resize-change-color {width:50px;height:50px;background:blue;} " + + ".change-color {width:50px;height:50px;background:yellow;} " + + ".add-class {}" + + "</style><div></div>"; +TEST_URL = "data:text/html;charset=utf8," + encodeURIComponent(TEST_URL); + +var test = makeTimelineTest("browser_timelineMarkers-frame-05.js", TEST_URL); diff --git a/docshell/test/browser/browser_timelineMarkers-frame-02.js b/docshell/test/browser/browser_timelineMarkers-frame-02.js new file mode 100644 index 0000000000..52d1e43782 --- /dev/null +++ b/docshell/test/browser/browser_timelineMarkers-frame-02.js @@ -0,0 +1,185 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* eslint-env mozilla/frame-script */ + +// This file expects frame-head.js to be loaded in the environment. +/* import-globals-from frame-head.js */ + +"use strict"; + +// Test that the docShell profile timeline API returns the right markers when +// restyles, reflows and paints occur + +function rectangleContains(rect, x, y, width, height) { + return ( + rect.x <= x && rect.y <= y && rect.width >= width && rect.height >= height + ); +} + +function sanitizeMarkers(list) { + // These markers are currently gathered from all docshells, which may + // interfere with this test. + return list.filter(e => e.name != "Worker" && e.name != "MinorGC"); +} + +var TESTS = [ + { + desc: "Changing the width of the test element", + searchFor: "Paint", + setup(docShell) { + let div = content.document.querySelector("div"); + div.setAttribute("class", "resize-change-color"); + }, + check(markers) { + markers = sanitizeMarkers(markers); + ok(!!markers.length, "markers were returned"); + console.log(markers); + info(JSON.stringify(markers.filter(m => m.name == "Paint"))); + ok( + markers.some(m => m.name == "Reflow"), + "markers includes Reflow" + ); + ok( + markers.some(m => m.name == "Paint"), + "markers includes Paint" + ); + for (let marker of markers.filter(m => m.name == "Paint")) { + // This change should generate at least one rectangle. + ok(marker.rectangles.length >= 1, "marker has one rectangle"); + // One of the rectangles should contain the div. + ok(marker.rectangles.some(r => rectangleContains(r, 0, 0, 100, 100))); + } + ok( + markers.some(m => m.name == "Styles"), + "markers includes Restyle" + ); + }, + }, + { + desc: "Changing the test element's background color", + searchFor: "Paint", + setup(docShell) { + let div = content.document.querySelector("div"); + div.setAttribute("class", "change-color"); + }, + check(markers) { + markers = sanitizeMarkers(markers); + ok(!!markers.length, "markers were returned"); + ok( + !markers.some(m => m.name == "Reflow"), + "markers doesn't include Reflow" + ); + ok( + markers.some(m => m.name == "Paint"), + "markers includes Paint" + ); + for (let marker of markers.filter(m => m.name == "Paint")) { + // This change should generate at least one rectangle. + ok(marker.rectangles.length >= 1, "marker has one rectangle"); + // One of the rectangles should contain the div. + ok(marker.rectangles.some(r => rectangleContains(r, 0, 0, 50, 50))); + } + ok( + markers.some(m => m.name == "Styles"), + "markers includes Restyle" + ); + }, + }, + { + desc: "Changing the test element's classname", + searchFor: "Paint", + setup(docShell) { + let div = content.document.querySelector("div"); + div.setAttribute("class", "change-color add-class"); + }, + check(markers) { + markers = sanitizeMarkers(markers); + ok(!!markers.length, "markers were returned"); + ok( + !markers.some(m => m.name == "Reflow"), + "markers doesn't include Reflow" + ); + ok( + !markers.some(m => m.name == "Paint"), + "markers doesn't include Paint" + ); + ok( + markers.some(m => m.name == "Styles"), + "markers includes Restyle" + ); + }, + }, + { + desc: "sync console.time/timeEnd", + searchFor: "ConsoleTime", + setup(docShell) { + content.console.time("FOOBAR"); + content.console.timeEnd("FOOBAR"); + let markers = docShell.popProfileTimelineMarkers(); + is(markers.length, 1, "Got one marker"); + is(markers[0].name, "ConsoleTime", "Got ConsoleTime marker"); + is(markers[0].causeName, "FOOBAR", "Got ConsoleTime FOOBAR detail"); + content.console.time("FOO"); + content.setTimeout(() => { + content.console.time("BAR"); + content.setTimeout(() => { + content.console.timeEnd("FOO"); + content.console.timeEnd("BAR"); + }, 100); + }, 100); + }, + check(markers) { + markers = sanitizeMarkers(markers); + is(markers.length, 2, "Got 2 markers"); + is(markers[0].name, "ConsoleTime", "Got first ConsoleTime marker"); + is(markers[0].causeName, "FOO", "Got ConsoleTime FOO detail"); + is(markers[1].name, "ConsoleTime", "Got second ConsoleTime marker"); + is(markers[1].causeName, "BAR", "Got ConsoleTime BAR detail"); + }, + }, + { + desc: "Timestamps created by console.timeStamp()", + searchFor: "Timestamp", + setup(docShell) { + content.console.timeStamp("rock"); + let markers = docShell.popProfileTimelineMarkers(); + is(markers.length, 1, "Got one marker"); + is(markers[0].name, "TimeStamp", "Got Timestamp marker"); + is(markers[0].causeName, "rock", "Got Timestamp label value"); + content.console.timeStamp("paper"); + content.console.timeStamp("scissors"); + content.console.timeStamp(); + content.console.timeStamp(undefined); + }, + check(markers) { + markers = sanitizeMarkers(markers); + is(markers.length, 4, "Got 4 markers"); + is(markers[0].name, "TimeStamp", "Got Timestamp marker"); + is(markers[0].causeName, "paper", "Got Timestamp label value"); + is(markers[1].name, "TimeStamp", "Got Timestamp marker"); + is(markers[1].causeName, "scissors", "Got Timestamp label value"); + is( + markers[2].name, + "TimeStamp", + "Got empty Timestamp marker when no argument given" + ); + is(markers[2].causeName, void 0, "Got empty Timestamp label value"); + is( + markers[3].name, + "TimeStamp", + "Got empty Timestamp marker when argument is undefined" + ); + is(markers[3].causeName, void 0, "Got empty Timestamp label value"); + markers.forEach(m => + is( + m.end, + m.start, + "All Timestamp markers should have identical start/end times" + ) + ); + }, + }, +]; + +timelineContentTest(TESTS); diff --git a/docshell/test/browser/browser_timelineMarkers-frame-03.js b/docshell/test/browser/browser_timelineMarkers-frame-03.js new file mode 100644 index 0000000000..4758df7fec --- /dev/null +++ b/docshell/test/browser/browser_timelineMarkers-frame-03.js @@ -0,0 +1,108 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* eslint-env mozilla/frame-script */ + +// This file expects frame-head.js to be loaded in the environment. +/* import-globals-from frame-head.js */ + +"use strict"; + +// Test that the docShell profile timeline API returns the right +// markers for DOM events. + +var TESTS = [ + { + desc: "Event dispatch with single handler", + searchFor: "DOMEvent", + setup(docShell) { + content.document.body.addEventListener( + "dog", + function(e) { + console.log("hi"); + }, + true + ); + content.document.body.dispatchEvent(new content.Event("dog")); + }, + check(markers) { + markers = markers.filter(m => m.name == "DOMEvent"); + is(markers.length, 1, "Got 1 marker"); + is(markers[0].type, "dog", "Got dog event name"); + is(markers[0].eventPhase, 2, "Got phase 2"); + }, + }, + { + desc: "Event dispatch with a second handler", + searchFor(markers) { + return markers.filter(m => m.name == "DOMEvent").length >= 2; + }, + setup(docShell) { + content.document.body.addEventListener("dog", function(e) { + console.log("hi"); + }); + content.document.body.dispatchEvent(new content.Event("dog")); + }, + check(markers) { + markers = markers.filter(m => m.name == "DOMEvent"); + is(markers.length, 2, "Got 2 markers"); + }, + }, + { + desc: "Event targeted at child", + searchFor(markers) { + return markers.filter(m => m.name == "DOMEvent").length >= 2; + }, + setup(docShell) { + let child = content.document.body.firstElementChild; + child.addEventListener("dog", function(e) {}); + child.dispatchEvent(new content.Event("dog")); + }, + check(markers) { + markers = markers.filter(m => m.name == "DOMEvent"); + is(markers.length, 2, "Got 2 markers"); + is(markers[0].eventPhase, 1, "Got phase 1 marker"); + is(markers[1].eventPhase, 2, "Got phase 2 marker"); + }, + }, + { + desc: "Event dispatch on a new document", + searchFor(markers) { + return markers.filter(m => m.name == "DOMEvent").length >= 2; + }, + setup(docShell) { + let doc = content.document.implementation.createHTMLDocument("doc"); + let p = doc.createElement("p"); + p.innerHTML = "inside"; + doc.body.appendChild(p); + + p.addEventListener("zebra", function(e) { + console.log("hi"); + }); + p.dispatchEvent(new content.Event("zebra")); + }, + check(markers) { + markers = markers.filter(m => m.name == "DOMEvent"); + is(markers.length, 1, "Got 1 marker"); + }, + }, + { + desc: "Event dispatch on window", + searchFor(markers) { + return markers.filter(m => m.name == "DOMEvent").length >= 2; + }, + setup(docShell) { + content.window.addEventListener("aardvark", function(e) { + console.log("I like ants!"); + }); + + content.window.dispatchEvent(new content.Event("aardvark")); + }, + check(markers) { + markers = markers.filter(m => m.name == "DOMEvent"); + is(markers.length, 1, "Got 1 marker"); + }, + }, +]; + +timelineContentTest(TESTS); diff --git a/docshell/test/browser/browser_timelineMarkers-frame-04.js b/docshell/test/browser/browser_timelineMarkers-frame-04.js new file mode 100644 index 0000000000..fb69a22054 --- /dev/null +++ b/docshell/test/browser/browser_timelineMarkers-frame-04.js @@ -0,0 +1,125 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* eslint-env mozilla/frame-script */ + +// This file expects frame-head.js to be loaded in the environment. +/* import-globals-from frame-head.js */ + +"use strict"; + +// Test that the docShell profile timeline API returns the right +// markers for XMLHttpRequest events. + +var TESTS = [ + { + desc: "Event dispatch from XMLHttpRequest", + searchFor(markers) { + return markers.filter(m => m.name == "DOMEvent").length >= 5; + }, + setup(docShell) { + content.dispatchEvent(new content.Event("dog")); + }, + check(markers) { + let domMarkers = markers.filter(m => m.name == "DOMEvent"); + // One subtlety here is that we have five events: the event we + // inject in "setup", plus the four state transition events. The + // first state transition is reported synchronously and so should + // show up as a nested marker. + is(domMarkers.length, 5, "Got 5 markers"); + + // We should see some Javascript markers, and they should have a + // cause. + let jsMarkers = markers.filter( + m => m.name == "Javascript" && m.causeName + ); + ok(!!jsMarkers.length, "Got some Javascript markers"); + is( + jsMarkers[0].stack.functionDisplayName, + "do_xhr", + "Javascript marker has entry point name" + ); + }, + }, +]; + +if ( + !Services.prefs.getBoolPref( + "javascript.options.asyncstack_capture_debuggee_only" + ) +) { + TESTS.push( + { + desc: "Async stack trace on Javascript marker", + searchFor: markers => { + return markers.some( + m => m.name == "Javascript" && m.causeName == "promise callback" + ); + }, + setup(docShell) { + content.dispatchEvent(new content.Event("promisetest")); + }, + check(markers) { + markers = markers.filter( + m => m.name == "Javascript" && m.causeName == "promise callback" + ); + ok(!!markers.length, "Found a Javascript marker"); + + let frame = markers[0].stack; + ok(frame.asyncParent !== null, "Parent frame has async parent"); + is( + frame.asyncParent.asyncCause, + "promise callback", + "Async parent has correct cause" + ); + let asyncFrame = frame.asyncParent; + // Skip over self-hosted parts of our Promise implementation. + while (asyncFrame.source === "self-hosted") { + asyncFrame = asyncFrame.parent; + } + is( + asyncFrame.functionDisplayName, + "do_promise", + "Async parent has correct function name" + ); + }, + }, + { + desc: "Async stack trace on Javascript marker with script", + searchFor: markers => { + return markers.some( + m => m.name == "Javascript" && m.causeName == "promise callback" + ); + }, + setup(docShell) { + content.dispatchEvent(new content.Event("promisescript")); + }, + check(markers) { + markers = markers.filter( + m => m.name == "Javascript" && m.causeName == "promise callback" + ); + ok(!!markers.length, "Found a Javascript marker"); + + let frame = markers[0].stack; + ok(frame.asyncParent !== null, "Parent frame has async parent"); + is( + frame.asyncParent.asyncCause, + "promise callback", + "Async parent has correct cause" + ); + let asyncFrame = frame.asyncParent; + // Skip over self-hosted parts of our Promise implementation. + while (asyncFrame.source === "self-hosted") { + asyncFrame = asyncFrame.parent; + } + is( + asyncFrame.functionDisplayName, + "do_promise_script", + "Async parent has correct function name" + ); + }, + } + ); +} + +timelineContentTest(TESTS); diff --git a/docshell/test/browser/browser_timelineMarkers-frame-05.js b/docshell/test/browser/browser_timelineMarkers-frame-05.js new file mode 100644 index 0000000000..e239df6382 --- /dev/null +++ b/docshell/test/browser/browser_timelineMarkers-frame-05.js @@ -0,0 +1,152 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* eslint-env mozilla/frame-script */ + +// This file expects frame-head.js to be loaded in the environment. +/* import-globals-from frame-head.js */ + +"use strict"; + +function forceSyncReflow(div) { + div.setAttribute("class", "resize-change-color"); + // Force a reflow. + return div.offsetWidth; +} + +function testSendingEvent() { + content.document.body.dispatchEvent(new content.Event("dog")); +} + +function testConsoleTime() { + content.console.time("cats"); +} + +function testConsoleTimeEnd() { + content.console.timeEnd("cats"); +} + +function makePromise() { + let resolver; + new Promise(function(resolve, reject) { + testConsoleTime(); + resolver = resolve; + }).then(function(val) { + testConsoleTimeEnd(); + }); + return resolver; +} + +function resolvePromise(resolver) { + resolver(23); +} + +var TESTS = [ + { + desc: "Stack trace on sync reflow", + searchFor: "Reflow", + setup(docShell) { + let div = content.document.querySelector("div"); + forceSyncReflow(div); + }, + check(markers) { + markers = markers.filter(m => m.name == "Reflow"); + ok(!!markers.length, "Reflow marker includes stack"); + ok(markers[0].stack.functionDisplayName == "forceSyncReflow"); + }, + }, + { + desc: "Stack trace on DOM event", + searchFor: "DOMEvent", + setup(docShell) { + content.document.body.addEventListener( + "dog", + function(e) { + console.log("hi"); + }, + true + ); + testSendingEvent(); + }, + check(markers) { + markers = markers.filter(m => m.name == "DOMEvent"); + ok(!!markers.length, "DOMEvent marker includes stack"); + ok( + markers[0].stack.functionDisplayName == "testSendingEvent", + "testSendingEvent is on the stack" + ); + }, + }, + { + desc: "Stack trace on console event", + searchFor: "ConsoleTime", + setup(docShell) { + testConsoleTime(); + testConsoleTimeEnd(); + }, + check(markers) { + markers = markers.filter(m => m.name == "ConsoleTime"); + ok(!!markers.length, "ConsoleTime marker includes stack"); + ok( + markers[0].stack.functionDisplayName == "testConsoleTime", + "testConsoleTime is on the stack" + ); + ok( + markers[0].endStack.functionDisplayName == "testConsoleTimeEnd", + "testConsoleTimeEnd is on the stack" + ); + }, + }, +]; + +if ( + !Services.prefs.getBoolPref( + "javascript.options.asyncstack_capture_debuggee_only" + ) +) { + TESTS.push({ + desc: "Async stack trace on Promise", + searchFor: "ConsoleTime", + setup(docShell) { + let resolver = makePromise(); + resolvePromise(resolver); + }, + check(markers) { + markers = markers.filter(m => m.name == "ConsoleTime"); + ok(!!markers.length, "Promise marker includes stack"); + ok( + markers[0].stack.functionDisplayName == "testConsoleTime", + "testConsoleTime is on the stack" + ); + let frame = markers[0].endStack; + ok( + frame.functionDisplayName == "testConsoleTimeEnd", + "testConsoleTimeEnd is on the stack" + ); + + frame = frame.parent; + ok( + frame.functionDisplayName == "makePromise/<", + "makePromise/< is on the stack" + ); + let asyncFrame = frame.asyncParent; + ok(asyncFrame !== null, "Frame has async parent"); + is( + asyncFrame.asyncCause, + "promise callback", + "Async parent has correct cause" + ); + // Skip over self-hosted parts of our Promise implementation. + while (asyncFrame.source === "self-hosted") { + asyncFrame = asyncFrame.parent; + } + is( + asyncFrame.functionDisplayName, + "makePromise", + "Async parent has correct function name" + ); + }, + }); +} + +timelineContentTest(TESTS); diff --git a/docshell/test/browser/browser_title_in_session_history.js b/docshell/test/browser/browser_title_in_session_history.js new file mode 100644 index 0000000000..bdcbbb7dfe --- /dev/null +++ b/docshell/test/browser/browser_title_in_session_history.js @@ -0,0 +1,63 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function test() { + const TEST_PAGE = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" + ) + "dummy_page.html"; + await BrowserTestUtils.withNewTab(TEST_PAGE, async browser => { + let titles = await ContentTask.spawn(browser, null, () => { + return new Promise(resolve => { + let titles = []; + content.document.body.innerHTML = "<div id='foo'>foo</div>"; + content.document.title = "Initial"; + content.history.pushState("1", "1", "1"); + content.document.title = "1"; + content.history.pushState("2", "2", "2"); + content.document.title = "2"; + content.location.hash = "hash"; + content.document.title = "3-hash"; + content.addEventListener( + "popstate", + () => { + content.addEventListener( + "popstate", + () => { + titles.push(content.document.title); + resolve(titles); + }, + { once: true } + ); + + titles.push(content.document.title); + // Test going forward a few steps. + content.history.go(2); + }, + { once: true } + ); + // Test going back a few steps. + content.history.go(-3); + }); + }); + is( + titles[0], + "3-hash", + "Document.title should have the value to which it was last time set." + ); + is( + titles[1], + "3-hash", + "Document.title should have the value to which it was last time set." + ); + let sh = browser.browsingContext.sessionHistory; + let count = sh.count; + is(sh.getEntryAtIndex(count - 1).title, "3-hash"); + is(sh.getEntryAtIndex(count - 2).title, "2"); + is(sh.getEntryAtIndex(count - 3).title, "1"); + is(sh.getEntryAtIndex(count - 4).title, "Initial"); + }); +}); diff --git a/docshell/test/browser/browser_ua_emulation.js b/docshell/test/browser/browser_ua_emulation.js new file mode 100644 index 0000000000..604f302179 --- /dev/null +++ b/docshell/test/browser/browser_ua_emulation.js @@ -0,0 +1,70 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const URL = "data:text/html;charset=utf-8,<iframe id='test-iframe'></iframe>"; + +// Test that the docShell UA emulation works +async function contentTaskNoOverride() { + let docshell = docShell; + is( + docshell.browsingContext.customUserAgent, + "", + "There should initially be no customUserAgent" + ); +} + +async function contentTaskOverride() { + let docshell = docShell; + is( + docshell.browsingContext.customUserAgent, + "foo", + "The user agent should be changed to foo" + ); + + is( + content.navigator.userAgent, + "foo", + "The user agent should be changed to foo" + ); + + let frameWin = content.document.querySelector("#test-iframe").contentWindow; + is( + frameWin.navigator.userAgent, + "foo", + "The UA should be passed on to frames." + ); + + let newFrame = content.document.createElement("iframe"); + content.document.body.appendChild(newFrame); + + let newFrameWin = newFrame.contentWindow; + is( + newFrameWin.navigator.userAgent, + "foo", + "Newly created frames should use the new UA" + ); + + newFrameWin.location.reload(); + await ContentTaskUtils.waitForEvent(newFrame, "load"); + + is( + newFrameWin.navigator.userAgent, + "foo", + "New UA should persist across reloads" + ); +} + +add_task(async function() { + await BrowserTestUtils.withNewTab({ gBrowser, url: URL }, async function( + browser + ) { + await SpecialPowers.spawn(browser, [], contentTaskNoOverride); + + let browsingContext = BrowserTestUtils.getBrowsingContextFrom(browser); + browsingContext.customUserAgent = "foo"; + + await SpecialPowers.spawn(browser, [], contentTaskOverride); + }); +}); diff --git a/docshell/test/browser/browser_uriFixupAlternateRedirects.js b/docshell/test/browser/browser_uriFixupAlternateRedirects.js new file mode 100644 index 0000000000..308d14a296 --- /dev/null +++ b/docshell/test/browser/browser_uriFixupAlternateRedirects.js @@ -0,0 +1,66 @@ +"use strict"; + +const { UrlbarTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/UrlbarTestUtils.sys.mjs" +); + +const REDIRECTURL = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://www.example.com/browser/docshell/test/browser/redirect_to_example.sjs"; + +add_task(async function() { + // Test both directly setting a value and pressing enter, or setting the + // value through input events, like the user would do. + const setValueFns = [ + value => { + gURLBar.value = value; + }, + value => { + return UrlbarTestUtils.promiseAutocompleteResultPopup({ + window, + waitForFocus, + value, + }); + }, + ]; + for (let setValueFn of setValueFns) { + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:blank" + ); + // Enter search terms and start a search. + gURLBar.focus(); + await setValueFn(REDIRECTURL); + let errorPageLoaded = BrowserTestUtils.waitForErrorPage(tab.linkedBrowser); + EventUtils.synthesizeKey("KEY_Enter"); + await errorPageLoaded; + let [contentURL, originalURL] = await SpecialPowers.spawn( + tab.linkedBrowser, + [], + () => { + return [ + content.document.documentURI, + content.document.mozDocumentURIIfNotForErrorPages.spec, + ]; + } + ); + info("Page that loaded: " + contentURL); + const errorURI = "about:neterror?"; + ok(contentURL.startsWith(errorURI), "Should be on an error page"); + + const contentPrincipal = tab.linkedBrowser.contentPrincipal; + ok( + contentPrincipal.spec.startsWith(errorURI), + "Principal should be for the error page" + ); + + originalURL = new URL(originalURL); + is( + originalURL.host, + "example", + "Should be an error for http://example, not http://www.example.com/" + ); + + BrowserTestUtils.removeTab(tab); + } +}); diff --git a/docshell/test/browser/browser_uriFixupIntegration.js b/docshell/test/browser/browser_uriFixupIntegration.js new file mode 100644 index 0000000000..1fce8a97c7 --- /dev/null +++ b/docshell/test/browser/browser_uriFixupIntegration.js @@ -0,0 +1,104 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { UrlbarTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/UrlbarTestUtils.sys.mjs" +); +const { SearchTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/SearchTestUtils.sys.mjs" +); + +SearchTestUtils.init(this); + +const kSearchEngineID = "browser_urifixup_search_engine"; +const kSearchEngineURL = "https://example.com/?search={searchTerms}"; +const kPrivateSearchEngineID = "browser_urifixup_search_engine_private"; +const kPrivateSearchEngineURL = "https://example.com/?private={searchTerms}"; + +add_setup(async function() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.search.separatePrivateDefault.ui.enabled", true], + ["browser.search.separatePrivateDefault", true], + ], + }); + + // Add new fake search engines. + await SearchTestUtils.installSearchExtension( + { + name: kSearchEngineID, + search_url: "https://example.com/", + search_url_get_params: "search={searchTerms}", + }, + { setAsDefault: true } + ); + + await SearchTestUtils.installSearchExtension( + { + name: kPrivateSearchEngineID, + search_url: "https://example.com/", + search_url_get_params: "private={searchTerms}", + }, + { setAsDefaultPrivate: true } + ); +}); + +add_task(async function test() { + // Test both directly setting a value and pressing enter, or setting the + // value through input events, like the user would do. + const setValueFns = [ + (value, win) => { + win.gURLBar.value = value; + }, + (value, win) => { + return UrlbarTestUtils.promiseAutocompleteResultPopup({ + window: win, + waitForFocus: SimpleTest.waitForFocus, + value, + }); + }, + ]; + + for (let value of ["foo bar", "brokenprotocol:somethingelse"]) { + for (let setValueFn of setValueFns) { + for (let inPrivateWindow of [false, true]) { + await do_test(value, setValueFn, inPrivateWindow); + } + } + } +}); + +async function do_test(value, setValueFn, inPrivateWindow) { + info(`Search ${value} in a ${inPrivateWindow ? "private" : "normal"} window`); + let win = await BrowserTestUtils.openNewBrowserWindow({ + private: inPrivateWindow, + }); + // Enter search terms and start a search. + win.gURLBar.focus(); + await setValueFn(value, win); + + EventUtils.synthesizeKey("KEY_Enter", {}, win); + + // Check that we load the correct URL. + let escapedValue = encodeURIComponent(value).replace("%20", "+"); + let searchEngineUrl = inPrivateWindow + ? kPrivateSearchEngineURL + : kSearchEngineURL; + let expectedURL = searchEngineUrl.replace("{searchTerms}", escapedValue); + await BrowserTestUtils.browserLoaded( + win.gBrowser.selectedBrowser, + false, + expectedURL + ); + // There should be at least one test. + Assert.equal( + win.gBrowser.selectedBrowser.currentURI.spec, + expectedURL, + "New tab should have loaded with expected url." + ); + + // Cleanup. + await BrowserTestUtils.closeWindow(win); +} diff --git a/docshell/test/browser/browser_viewsource_chrome_to_content.js b/docshell/test/browser/browser_viewsource_chrome_to_content.js new file mode 100644 index 0000000000..5c73f75a47 --- /dev/null +++ b/docshell/test/browser/browser_viewsource_chrome_to_content.js @@ -0,0 +1,20 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); +const TEST_URI = `view-source:${TEST_PATH}dummy_page.html`; + +add_task(async function chrome_to_content_view_source() { + await BrowserTestUtils.withNewTab("about:mozilla", async browser => { + is(browser.documentURI.spec, "about:mozilla"); + + // This process switch would previously crash in debug builds due to assertion failures. + BrowserTestUtils.loadURI(browser, TEST_URI); + await BrowserTestUtils.browserLoaded(browser); + is(browser.documentURI.spec, TEST_URI); + }); +}); diff --git a/docshell/test/browser/browser_viewsource_multipart.js b/docshell/test/browser/browser_viewsource_multipart.js new file mode 100644 index 0000000000..4c1d74f2d5 --- /dev/null +++ b/docshell/test/browser/browser_viewsource_multipart.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); +const MULTIPART_URI = `${TEST_PATH}file_basic_multipart.sjs`; + +add_task(async function viewsource_multipart_uri() { + await BrowserTestUtils.withNewTab("about:blank", async browser => { + BrowserTestUtils.loadURI(browser, MULTIPART_URI); + await BrowserTestUtils.browserLoaded(browser); + is(browser.currentURI.spec, MULTIPART_URI); + + // Continue probing the URL until we find the h1 we're expecting. This + // should handle cases where we somehow beat the second document having + // loaded. + await TestUtils.waitForCondition(async () => { + let value = await SpecialPowers.spawn(browser, [], async () => { + let headers = content.document.querySelectorAll("h1"); + is(headers.length, 1, "only one h1 should be present"); + return headers[0].textContent; + }); + + ok(value == "First" || value == "Second", "some other value was found?"); + return value == "Second"; + }); + + // Load a view-source version of the page, which should show the full + // content, not handling multipart. + BrowserTestUtils.loadURI(browser, `view-source:${MULTIPART_URI}`); + await BrowserTestUtils.browserLoaded(browser); + + let viewSourceContent = await SpecialPowers.spawn(browser, [], async () => { + return content.document.body.textContent; + }); + + ok(viewSourceContent.includes("<h1>First</h1>"), "first header"); + ok(viewSourceContent.includes("<h1>Second</h1>"), "second header"); + ok(viewSourceContent.includes("BOUNDARY"), "boundary"); + }); +}); diff --git a/docshell/test/browser/dummy_iframe_page.html b/docshell/test/browser/dummy_iframe_page.html new file mode 100644 index 0000000000..12ce921856 --- /dev/null +++ b/docshell/test/browser/dummy_iframe_page.html @@ -0,0 +1,8 @@ +<html> +<head> <meta charset="utf-8"> </head> + <body> + just a dummy html file with an iframe + <iframe id="frame1" src="dummy_page.html?sub_entry=0"></iframe> + <iframe id="frame2" src="dummy_page.html?sub_entry=0"></iframe> + </body> +</html> diff --git a/docshell/test/browser/dummy_page.html b/docshell/test/browser/dummy_page.html new file mode 100644 index 0000000000..59bf2a5f8f --- /dev/null +++ b/docshell/test/browser/dummy_page.html @@ -0,0 +1,6 @@ +<html> +<head> <meta charset="utf-8"> </head> + <body> + just a dummy html file + </body> +</html> diff --git a/docshell/test/browser/favicon_bug655270.ico b/docshell/test/browser/favicon_bug655270.ico Binary files differnew file mode 100644 index 0000000000..d44438903b --- /dev/null +++ b/docshell/test/browser/favicon_bug655270.ico diff --git a/docshell/test/browser/file_backforward_restore_scroll.html b/docshell/test/browser/file_backforward_restore_scroll.html new file mode 100644 index 0000000000..5a40b36c10 --- /dev/null +++ b/docshell/test/browser/file_backforward_restore_scroll.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> +</head> +<body> + <iframe src="http://mochi.test:8888/"></iframe> + <iframe src="http://example.com/"></iframe> +</body> +</html> diff --git a/docshell/test/browser/file_backforward_restore_scroll.html^headers^ b/docshell/test/browser/file_backforward_restore_scroll.html^headers^ new file mode 100644 index 0000000000..59ba296103 --- /dev/null +++ b/docshell/test/browser/file_backforward_restore_scroll.html^headers^ @@ -0,0 +1 @@ +Cache-control: no-store diff --git a/docshell/test/browser/file_basic_multipart.sjs b/docshell/test/browser/file_basic_multipart.sjs new file mode 100644 index 0000000000..5e89b93948 --- /dev/null +++ b/docshell/test/browser/file_basic_multipart.sjs @@ -0,0 +1,24 @@ +"use strict"; + +function handleRequest(request, response) { + response.setHeader( + "Content-Type", + "multipart/x-mixed-replace;boundary=BOUNDARY", + false + ); + response.setStatusLine(request.httpVersion, 200, "OK"); + + response.write(`--BOUNDARY +Content-Type: text/html + +<h1>First</h1> +Will be replaced +--BOUNDARY +Content-Type: text/html + +<h1>Second</h1> +This will stick around +--BOUNDARY +--BOUNDARY-- +`); +} diff --git a/docshell/test/browser/file_bug1046022.html b/docshell/test/browser/file_bug1046022.html new file mode 100644 index 0000000000..27a1e1f079 --- /dev/null +++ b/docshell/test/browser/file_bug1046022.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>Bug 1046022 - test navigating inside onbeforeunload</title> + </head> + <body> + Waiting for onbeforeunload to hit... + </body> + + <script> +var testFns = [ + function(e) { + e.target.location.href = "otherpage-href-set.html"; + return "stop"; + }, + function(e) { + e.target.location.reload(); + return "stop"; + }, + function(e) { + e.currentTarget.stop(); + return "stop"; + }, + function(e) { + e.target.location.replace("otherpage-location-replaced.html"); + return "stop"; + }, + function(e) { + var link = e.target.createElement("a"); + link.href = "otherpage.html"; + e.target.body.appendChild(link); + link.click(); + return "stop"; + }, + function(e) { + var link = e.target.createElement("a"); + link.href = "otherpage.html"; + link.setAttribute("target", "_blank"); + e.target.body.appendChild(link); + link.click(); + return "stop"; + }, + function(e) { + var link = e.target.createElement("a"); + link.href = e.target.location.href; + e.target.body.appendChild(link); + link.setAttribute("target", "somearbitrarywindow"); + link.click(); + return "stop"; + }, +]; + </script> +</html> diff --git a/docshell/test/browser/file_bug1206879.html b/docshell/test/browser/file_bug1206879.html new file mode 100644 index 0000000000..5313902a9b --- /dev/null +++ b/docshell/test/browser/file_bug1206879.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html> + <head> + <title>Test page for bug 1206879</title> + </head> + <body> + <iframe src="http://example.com/"></iframe> + </body> +</html> diff --git a/docshell/test/browser/file_bug1328501.html b/docshell/test/browser/file_bug1328501.html new file mode 100644 index 0000000000..517ef53e02 --- /dev/null +++ b/docshell/test/browser/file_bug1328501.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Page with iframes</title> + <script type="application/javascript"> + let promiseResolvers = { + "testFrame1": {}, + "testFrame2": {}, + }; + let promises = [ + new Promise(r => promiseResolvers.testFrame1.resolve = r), + new Promise(r => promiseResolvers.testFrame2.resolve = r), + ]; + function frameLoaded(frame) { + promiseResolvers[frame].resolve(); + } + Promise.all(promises).then(() => window.dispatchEvent(new Event("frames-loaded"))); + </script> + </head> + <body onunload=""> + <div> + <iframe id="testFrame1" src="dummy_page.html" onload="frameLoaded(this.id);" ></iframe> + <iframe id="testFrame2" src="dummy_page.html" onload="frameLoaded(this.id);" ></iframe> + </div> + </body> +</html> diff --git a/docshell/test/browser/file_bug1328501_frame.html b/docshell/test/browser/file_bug1328501_frame.html new file mode 100644 index 0000000000..156dd41eaa --- /dev/null +++ b/docshell/test/browser/file_bug1328501_frame.html @@ -0,0 +1,4 @@ +<!DOCTYPE html> +<html lang="en"> + <body>Subframe page for testing</body> +</html> diff --git a/docshell/test/browser/file_bug1328501_framescript.js b/docshell/test/browser/file_bug1328501_framescript.js new file mode 100644 index 0000000000..19c86c75e7 --- /dev/null +++ b/docshell/test/browser/file_bug1328501_framescript.js @@ -0,0 +1,38 @@ +// Forward iframe loaded event. + +/* eslint-env mozilla/frame-script */ + +addEventListener( + "frames-loaded", + e => sendAsyncMessage("test:frames-loaded"), + true, + true +); + +let requestObserver = { + observe(subject, topic, data) { + if (topic == "http-on-opening-request") { + // Get DOMWindow on all child docshells to force about:blank + // content viewers being created. + getChildDocShells().map(ds => { + ds + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsILoadContext).associatedWindow; + }); + } + }, + QueryInterface: ChromeUtils.generateQI(["nsIObserver"]), +}; +Services.obs.addObserver(requestObserver, "http-on-opening-request"); +addEventListener("unload", e => { + if (e.target == this) { + Services.obs.removeObserver(requestObserver, "http-on-opening-request"); + } +}); + +function getChildDocShells() { + return docShell.getAllDocShellsInSubtree( + Ci.nsIDocShellTreeItem.typeAll, + Ci.nsIDocShell.ENUMERATE_FORWARDS + ); +} diff --git a/docshell/test/browser/file_bug1543077-3-child.html b/docshell/test/browser/file_bug1543077-3-child.html new file mode 100644 index 0000000000..858a4623ed --- /dev/null +++ b/docshell/test/browser/file_bug1543077-3-child.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>No encoding declaration in parent or child</title> +</head> +<body> +<p>Hiragana letter a if decoded as ISO-2022-JP: $B$"(B</p> +</body> +</html> + diff --git a/docshell/test/browser/file_bug1543077-3.html b/docshell/test/browser/file_bug1543077-3.html new file mode 100644 index 0000000000..c4f467dd3f --- /dev/null +++ b/docshell/test/browser/file_bug1543077-3.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>No encoding declaration in parent or child</title> +</head> +<body> +<h1>No encoding declaration in parent or child</h1> + +<p>Hiragana letter a if decoded as ISO-2022-JP: $B$"(B</p> + +<iframe src="file_bug1543077-3-child.html"></iframe> + +</body> +</html> + diff --git a/docshell/test/browser/file_bug1622420.html b/docshell/test/browser/file_bug1622420.html new file mode 100644 index 0000000000..63beb38302 --- /dev/null +++ b/docshell/test/browser/file_bug1622420.html @@ -0,0 +1 @@ +<iframe src="http://example.com/"></iframe> diff --git a/docshell/test/browser/file_bug1648464-1-child.html b/docshell/test/browser/file_bug1648464-1-child.html new file mode 100644 index 0000000000..7bb1ad965b --- /dev/null +++ b/docshell/test/browser/file_bug1648464-1-child.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset=windows-1252> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>windows-1252 in parent and child, actually EUC-JP</title> +</head> +<body> +<p>Hiragana letter a if decoded as EUC-JP: </p> +<p>ʸ¸Ǥ</p> +</body> +</html> + diff --git a/docshell/test/browser/file_bug1648464-1.html b/docshell/test/browser/file_bug1648464-1.html new file mode 100644 index 0000000000..2051cf61ed --- /dev/null +++ b/docshell/test/browser/file_bug1648464-1.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset=windows-1252> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>windows-1252 in parent and child, actually EUC-JP</title> +</head> +<body> +<h1>windows-1252 in parent and child, actually EUC-JP</h1> + +<p>Hiragana letter a if decoded as EUC-JP: </p> +<p>ʸ¸Ǥ</p> + +<iframe src="file_bug1648464-1-child.html"></iframe> + +</body> +</html> + diff --git a/docshell/test/browser/file_bug1673702.json b/docshell/test/browser/file_bug1673702.json new file mode 100644 index 0000000000..6d7227eb1f --- /dev/null +++ b/docshell/test/browser/file_bug1673702.json @@ -0,0 +1 @@ +{ "version": 1, } diff --git a/docshell/test/browser/file_bug1673702.json^headers^ b/docshell/test/browser/file_bug1673702.json^headers^ new file mode 100644 index 0000000000..6010bfd188 --- /dev/null +++ b/docshell/test/browser/file_bug1673702.json^headers^ @@ -0,0 +1 @@ +Content-Type: application/json; charset=utf-8 diff --git a/docshell/test/browser/file_bug1688368-1.sjs b/docshell/test/browser/file_bug1688368-1.sjs new file mode 100644 index 0000000000..0693b7970c --- /dev/null +++ b/docshell/test/browser/file_bug1688368-1.sjs @@ -0,0 +1,44 @@ +"use strict"; + +const DELAY = 1 * 1000; // Delay one second before completing the request. + +let nsTimer = Components.Constructor( + "@mozilla.org/timer;1", + "nsITimer", + "initWithCallback" +); + +let timer; + +function handleRequest(request, response) { + response.processAsync(); + + response.setHeader("Content-Type", "text/html", false); + response.setHeader("Cache-Control", "no-cache", false); + response.write(`<!DOCTYPE html> +<html> +<head> + <title>UTF-8 file, 1024 bytes long!</title> +</head> +<body>`); + + // Note: We need to store a reference to the timer to prevent it from being + // canceled when it's GCed. + timer = new nsTimer( + () => { + var snowmen = + "\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083"; + response.write( + snowmen + + ` +</body> +</html> + +` + ); + response.finish(); + }, + DELAY, + Ci.nsITimer.TYPE_ONE_SHOT + ); +} diff --git a/docshell/test/browser/file_bug1691153.html b/docshell/test/browser/file_bug1691153.html new file mode 100644 index 0000000000..dea144eb41 --- /dev/null +++ b/docshell/test/browser/file_bug1691153.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="UTF-8"> + <title>bug 1691153</title> +</head> +<body> +<h1>bug 1691153</h1> +<script> +function toBlobURL(data, mimeType) { + return URL.createObjectURL( + new Blob([data], { + type: mimeType, + }) + ); +} +// closing script element literal split up to not end the parent script element +let testurl = toBlobURL("<body></body>", "text/html"); +addEventListener("message", event => { + if (event.data == "getblob") { + postMessage({ bloburl: testurl }, "*"); + } +}); +// the blob URL should have a content principal +</script> +</body> +</html> diff --git a/docshell/test/browser/file_bug1716290-1.sjs b/docshell/test/browser/file_bug1716290-1.sjs new file mode 100644 index 0000000000..83e6eede3d --- /dev/null +++ b/docshell/test/browser/file_bug1716290-1.sjs @@ -0,0 +1,21 @@ +function handleRequest(request, response) { + if (getState("reloaded") == "reloaded") { + response.setHeader( + "Content-Type", + "text/html; charset=windows-1254", + false + ); + response.write("\u00E4"); + } else { + response.setHeader("Content-Type", "text/html; charset=Shift_JIS", false); + if (getState("loaded") == "loaded") { + setState("reloaded", "reloaded"); + } else { + setState("loaded", "loaded"); + } + // kilobyte to force late-detection reload + response.write("a".repeat(1024)); + response.write("<body>"); + response.write("\u00E4"); + } +} diff --git a/docshell/test/browser/file_bug1716290-2.sjs b/docshell/test/browser/file_bug1716290-2.sjs new file mode 100644 index 0000000000..e695259e30 --- /dev/null +++ b/docshell/test/browser/file_bug1716290-2.sjs @@ -0,0 +1,18 @@ +function handleRequest(request, response) { + if (getState("reloaded") == "reloaded") { + response.setHeader("Content-Type", "text/html", false); + response.write("<meta charset=iso-2022-kr>\u00E4"); + } else { + response.setHeader("Content-Type", "text/html", false); + if (getState("loaded") == "loaded") { + setState("reloaded", "reloaded"); + } else { + setState("loaded", "loaded"); + } + response.write("<meta charset=Shift_JIS>"); + // kilobyte to force late-detection reload + response.write("a".repeat(1024)); + response.write("<body>"); + response.write("\u00E4"); + } +} diff --git a/docshell/test/browser/file_bug1716290-3.sjs b/docshell/test/browser/file_bug1716290-3.sjs new file mode 100644 index 0000000000..7a302e05e4 --- /dev/null +++ b/docshell/test/browser/file_bug1716290-3.sjs @@ -0,0 +1,17 @@ +function handleRequest(request, response) { + if (getState("reloaded") == "reloaded") { + response.setHeader("Content-Type", "text/html; charset=iso-2022-kr", false); + response.write("\u00E4"); + } else { + response.setHeader("Content-Type", "text/html; charset=Shift_JIS", false); + if (getState("loaded") == "loaded") { + setState("reloaded", "reloaded"); + } else { + setState("loaded", "loaded"); + } + // kilobyte to force late-detection reload + response.write("a".repeat(1024)); + response.write("<body>"); + response.write("\u00E4"); + } +} diff --git a/docshell/test/browser/file_bug1716290-4.sjs b/docshell/test/browser/file_bug1716290-4.sjs new file mode 100644 index 0000000000..36753ef532 --- /dev/null +++ b/docshell/test/browser/file_bug1716290-4.sjs @@ -0,0 +1,17 @@ +function handleRequest(request, response) { + if (getState("reloaded") == "reloaded") { + response.setHeader("Content-Type", "text/html", false); + response.write("\u00FE\u00FF\u00E4"); + } else { + response.setHeader("Content-Type", "text/html; charset=Shift_JIS", false); + if (getState("loaded") == "loaded") { + setState("reloaded", "reloaded"); + } else { + setState("loaded", "loaded"); + } + // kilobyte to force late-detection reload + response.write("a".repeat(1024)); + response.write("<body>"); + response.write("\u00E4"); + } +} diff --git a/docshell/test/browser/file_bug1736248-1.html b/docshell/test/browser/file_bug1736248-1.html new file mode 100644 index 0000000000..177acb8f77 --- /dev/null +++ b/docshell/test/browser/file_bug1736248-1.html @@ -0,0 +1,4 @@ +Kilobyte of ASCII followed by UTF-8. +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +Hej världen! diff --git a/docshell/test/browser/file_bug234628-1-child.html b/docshell/test/browser/file_bug234628-1-child.html new file mode 100644 index 0000000000..c36197ac4f --- /dev/null +++ b/docshell/test/browser/file_bug234628-1-child.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>No encoding declaration in parent or child</title> +</head> +<body> +<p>Euro sign if decoded as Windows-1252: </p> +<p>a with diaeresis if decoded as Windows-1252: </p> +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-1.html b/docshell/test/browser/file_bug234628-1.html new file mode 100644 index 0000000000..11c523ccd9 --- /dev/null +++ b/docshell/test/browser/file_bug234628-1.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>No encoding declaration in parent or child</title> +</head> +<body> +<h1>No encoding declaration in parent or child</h1> + +<p>Euro sign if decoded as Windows-1252: </p> +<p>a with diaeresis if decoded as Windows-1252: </p> + +<iframe src="file_bug234628-1-child.html"></iframe> + +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-10-child.xhtml b/docshell/test/browser/file_bug234628-10-child.xhtml new file mode 100644 index 0000000000..cccf6f2bc0 --- /dev/null +++ b/docshell/test/browser/file_bug234628-10-child.xhtml @@ -0,0 +1,4 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head><title>XML child with no encoding declaration</title></head> +<body><p>Euro sign if decoded as UTF-8: €</p></body> +</html> diff --git a/docshell/test/browser/file_bug234628-10.html b/docshell/test/browser/file_bug234628-10.html new file mode 100644 index 0000000000..78b8f0035d --- /dev/null +++ b/docshell/test/browser/file_bug234628-10.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>No encoding declaration in HTML parent or XHTML child</title> +</head> +<body> +<h1>No encoding declaration in HTML parent or XHTML child</h1> + +<p>Euro sign if decoded as Windows-1252: </p> +<p>a with diaeresis if decoded as Windows-1252: </p> + +<iframe src="file_bug234628-10-child.xhtml"></iframe> + +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-11-child.xhtml b/docshell/test/browser/file_bug234628-11-child.xhtml new file mode 100644 index 0000000000..11ef668b0c --- /dev/null +++ b/docshell/test/browser/file_bug234628-11-child.xhtml @@ -0,0 +1,4 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head><title>No encoding declaration in HTML parent and HTTP declaration in XHTML child</title></head> +<body><p>Euro sign if decoded as UTF-8: €</p></body> +</html> diff --git a/docshell/test/browser/file_bug234628-11-child.xhtml^headers^ b/docshell/test/browser/file_bug234628-11-child.xhtml^headers^ new file mode 100644 index 0000000000..30fb304056 --- /dev/null +++ b/docshell/test/browser/file_bug234628-11-child.xhtml^headers^ @@ -0,0 +1 @@ +Content-Type: application/xhtml+xml; charset=utf-8 diff --git a/docshell/test/browser/file_bug234628-11.html b/docshell/test/browser/file_bug234628-11.html new file mode 100644 index 0000000000..21c5b733e0 --- /dev/null +++ b/docshell/test/browser/file_bug234628-11.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>No encoding declaration in HTML parent and HTTP declaration in XHTML child</title> +</head> +<body> +<h1>No encoding declaration in HTML parent and HTTP declaration in XHTML child</h1> + +<p>Euro sign if decoded as Windows-1252: </p> +<p>a with diaeresis if decoded as Windows-1252: </p> + +<iframe src="file_bug234628-11-child.xhtml"></iframe> + +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-2-child.html b/docshell/test/browser/file_bug234628-2-child.html new file mode 100644 index 0000000000..0acd2e0b27 --- /dev/null +++ b/docshell/test/browser/file_bug234628-2-child.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>No encoding declaration in parent or child</title> +</head> +<body> +<p>Euro sign if decoded as UTF-8: €</p> +<p>a with diaeresis if decoded as UTF-8: ä</p> +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-2.html b/docshell/test/browser/file_bug234628-2.html new file mode 100644 index 0000000000..a87d29e126 --- /dev/null +++ b/docshell/test/browser/file_bug234628-2.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>No encoding declaration in parent or child</title> +</head> +<body> +<h1>No encoding declaration in parent or child</h1> + +<p>Euro sign if decoded as Windows-1252: </p> +<p>a with diaeresis if decoded as Windows-1252: </p> + +<iframe src="file_bug234628-2-child.html"></iframe> + +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-3-child.html b/docshell/test/browser/file_bug234628-3-child.html new file mode 100644 index 0000000000..a6ad832310 --- /dev/null +++ b/docshell/test/browser/file_bug234628-3-child.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>meta declaration in parent and child</title> +</head> +<body> +<p>Euro sign if decoded as UTF-8: €</p> +<p>a with diaeresis if decoded as UTF-8: ä</p> +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-3.html b/docshell/test/browser/file_bug234628-3.html new file mode 100644 index 0000000000..8caab60402 --- /dev/null +++ b/docshell/test/browser/file_bug234628-3.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="windows-1252"> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>meta declaration in parent and child</title> +</head> +<body> +<h1>meta declaration in parent and child</h1> + +<p>Euro sign if decoded as Windows-1252: </p> +<p>a with diaeresis if decoded as Windows-1252: </p> + +<iframe src="file_bug234628-3-child.html"></iframe> + +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-4-child.html b/docshell/test/browser/file_bug234628-4-child.html new file mode 100644 index 0000000000..f0e7c2c058 --- /dev/null +++ b/docshell/test/browser/file_bug234628-4-child.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>meta declaration in parent and BOM in child</title> +</head> +<body> +<p>Euro sign if decoded as UTF-8: €</p> +<p>a with diaeresis if decoded as UTF-8: ä</p> +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-4.html b/docshell/test/browser/file_bug234628-4.html new file mode 100644 index 0000000000..0137579010 --- /dev/null +++ b/docshell/test/browser/file_bug234628-4.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="windows-1252"> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>meta declaration in parent and BOM in child</title> +</head> +<body> +<h1>meta declaration in parent and BOM in child</h1> + +<p>Euro sign if decoded as Windows-1252: </p> +<p>a with diaeresis if decoded as Windows-1252: </p> + +<iframe src="file_bug234628-4-child.html"></iframe> + +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-5-child.html b/docshell/test/browser/file_bug234628-5-child.html Binary files differnew file mode 100644 index 0000000000..a650552f63 --- /dev/null +++ b/docshell/test/browser/file_bug234628-5-child.html diff --git a/docshell/test/browser/file_bug234628-5.html b/docshell/test/browser/file_bug234628-5.html new file mode 100644 index 0000000000..987e6420be --- /dev/null +++ b/docshell/test/browser/file_bug234628-5.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="windows-1252"> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>meta declaration in parent and UTF-16 BOM in child</title> +</head> +<body> +<h1>meta declaration in parent and UTF-16 BOM in child</h1> + +<p>Euro sign if decoded as Windows-1252: </p> +<p>a with diaeresis if decoded as Windows-1252: </p> + +<iframe src="file_bug234628-5-child.html"></iframe> + +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-6-child.html b/docshell/test/browser/file_bug234628-6-child.html Binary files differnew file mode 100644 index 0000000000..52c37f2596 --- /dev/null +++ b/docshell/test/browser/file_bug234628-6-child.html diff --git a/docshell/test/browser/file_bug234628-6-child.html^headers^ b/docshell/test/browser/file_bug234628-6-child.html^headers^ new file mode 100644 index 0000000000..bfdcf487fb --- /dev/null +++ b/docshell/test/browser/file_bug234628-6-child.html^headers^ @@ -0,0 +1 @@ +Content-Type: text/html; charset=utf-16be diff --git a/docshell/test/browser/file_bug234628-6.html b/docshell/test/browser/file_bug234628-6.html new file mode 100644 index 0000000000..9d7fc580c3 --- /dev/null +++ b/docshell/test/browser/file_bug234628-6.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="windows-1252"> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>meta declaration in parent and BOMless UTF-16 with HTTP charset in child</title> +</head> +<body> +<h1>meta declaration in parent and BOMless UTF-16 with HTTP charset in child</h1> + +<p>Euro sign if decoded as Windows-1252: </p> +<p>a with diaeresis if decoded as Windows-1252: </p> + +<iframe src="file_bug234628-6-child.html"></iframe> + +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-8-child.html b/docshell/test/browser/file_bug234628-8-child.html new file mode 100644 index 0000000000..254e0fb2b3 --- /dev/null +++ b/docshell/test/browser/file_bug234628-8-child.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>meta declaration in parent and no declaration in child</title> +</head> +<body> +<p>Capital dje if decoded as Windows-1251: </p> + +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-8.html b/docshell/test/browser/file_bug234628-8.html new file mode 100644 index 0000000000..b44e91801c --- /dev/null +++ b/docshell/test/browser/file_bug234628-8.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="windows-1251"> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>meta declaration in parent and no declaration in child</title> +</head> +<body> +<h1>meta declaration in parent and no declaration in child</h1> + +<p>Capital dje if decoded as Windows-1251: </p> + +<iframe src="file_bug234628-8-child.html"></iframe> + +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-9-child.html b/docshell/test/browser/file_bug234628-9-child.html new file mode 100644 index 0000000000..a86b14d7ee --- /dev/null +++ b/docshell/test/browser/file_bug234628-9-child.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>UTF-16 with BOM in parent and no declaration in child</title> +</head> +<body> +<p>Euro sign if decoded as Windows-1251: </p> + +</body> +</html> + diff --git a/docshell/test/browser/file_bug234628-9.html b/docshell/test/browser/file_bug234628-9.html Binary files differnew file mode 100644 index 0000000000..8a469da3aa --- /dev/null +++ b/docshell/test/browser/file_bug234628-9.html diff --git a/docshell/test/browser/file_bug420605.html b/docshell/test/browser/file_bug420605.html new file mode 100644 index 0000000000..8424b92f8f --- /dev/null +++ b/docshell/test/browser/file_bug420605.html @@ -0,0 +1,31 @@ +<head> +<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg=="/> + <title>Page Title for Bug 420605</title> +</head> +<body> + <h1>Fragment links</h1> + + <p>This page has a bunch of fragment links to sections below:</p> + + <ul> + <li><a id="firefox-link" href="#firefox">Firefox</a></li> + <li><a id="thunderbird-link" href="#thunderbird">Thunderbird</a></li> + <li><a id="seamonkey-link" href="#seamonkey">Seamonkey</a></li> + </ul> + + <p>And here are the sections:</p> + + <h2 id="firefox">Firefox</h2> + + <p>Firefox is a browser.</p> + + <h2 id="thunderbird">Thunderbird</h2> + + <p>Thunderbird is an email client</p> + + <h2 id="seamonkey">Seamonkey</h2> + + <p>Seamonkey is the all-in-one application.</p> + +</body> +</html> diff --git a/docshell/test/browser/file_bug503832.html b/docshell/test/browser/file_bug503832.html new file mode 100644 index 0000000000..338631c8a0 --- /dev/null +++ b/docshell/test/browser/file_bug503832.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> +<!-- +Test page for https://bugzilla.mozilla.org/show_bug.cgi?id=503832 +--> +<head> + <title>Page Title for Bug 503832</title> +</head> +<body> + <h1>Fragment links</h1> + + <p>This page has a bunch of fragment links to sections below:</p> + + <ul> + <li><a id="firefox-link" href="#firefox">Firefox</a></li> + <li><a id="thunderbird-link" href="#thunderbird">Thunderbird</a></li> + <li><a id="seamonkey-link" href="#seamonkey">Seamonkey</a></li> + </ul> + + <p>And here are the sections:</p> + + <h2 id="firefox">Firefox</h2> + + <p>Firefox is a browser.</p> + + <h2 id="thunderbird">Thunderbird</h2> + + <p>Thunderbird is an email client</p> + + <h2 id="seamonkey">Seamonkey</h2> + + <p>Seamonkey is the all-in-one application.</p> + +</body> +</html> diff --git a/docshell/test/browser/file_bug655270.html b/docshell/test/browser/file_bug655270.html new file mode 100644 index 0000000000..0c08d982b1 --- /dev/null +++ b/docshell/test/browser/file_bug655270.html @@ -0,0 +1,11 @@ +<html> + +<head> + <link rel='icon' href='favicon_bug655270.ico'> +</head> + +<body> +Nothing to see here... +</body> + +</html> diff --git a/docshell/test/browser/file_bug670318.html b/docshell/test/browser/file_bug670318.html new file mode 100644 index 0000000000..a78e8fcb19 --- /dev/null +++ b/docshell/test/browser/file_bug670318.html @@ -0,0 +1,23 @@ +<html><head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<script> +function load() { + function next() { + if (count < 5) + iframe.src = "data:text/html;charset=utf-8,iframe " + (++count); + } + + var count = 0; + var iframe = document.createElement("iframe"); + iframe.onload = function() { setTimeout(next, 0); }; + document.body.appendChild(iframe); + + setTimeout(next, 0); +} +</script> +</head> + +<body onload="load()"> +Testcase +</body> +</html> diff --git a/docshell/test/browser/file_bug673087-1-child.html b/docshell/test/browser/file_bug673087-1-child.html new file mode 100644 index 0000000000..7bb1ad965b --- /dev/null +++ b/docshell/test/browser/file_bug673087-1-child.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset=windows-1252> +<meta content="width=device-width, initial-scale=1" name="viewport"> +<title>windows-1252 in parent and child, actually EUC-JP</title> +</head> +<body> +<p>Hiragana letter a if decoded as EUC-JP: </p> +<p>ʸ¸Ǥ</p> +</body> +</html> + diff --git a/docshell/test/browser/file_bug673087-1.html b/docshell/test/browser/file_bug673087-1.html Binary files differnew file mode 100644 index 0000000000..3dbea43d66 --- /dev/null +++ b/docshell/test/browser/file_bug673087-1.html diff --git a/docshell/test/browser/file_bug673087-1.html^headers^ b/docshell/test/browser/file_bug673087-1.html^headers^ new file mode 100644 index 0000000000..2340a89c93 --- /dev/null +++ b/docshell/test/browser/file_bug673087-1.html^headers^ @@ -0,0 +1 @@ +Content-Type: text/html; charset=windows-1252 diff --git a/docshell/test/browser/file_bug673087-2.html b/docshell/test/browser/file_bug673087-2.html new file mode 100644 index 0000000000..ccbf896ca6 --- /dev/null +++ b/docshell/test/browser/file_bug673087-2.html @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="ISO-2022-KR"> +FAIL
\ No newline at end of file diff --git a/docshell/test/browser/file_bug852909.pdf b/docshell/test/browser/file_bug852909.pdf Binary files differnew file mode 100644 index 0000000000..89066463f1 --- /dev/null +++ b/docshell/test/browser/file_bug852909.pdf diff --git a/docshell/test/browser/file_bug852909.png b/docshell/test/browser/file_bug852909.png Binary files differnew file mode 100644 index 0000000000..c7510d388f --- /dev/null +++ b/docshell/test/browser/file_bug852909.png diff --git a/docshell/test/browser/file_click_link_within_view_source.html b/docshell/test/browser/file_click_link_within_view_source.html new file mode 100644 index 0000000000..d78e4ba0ff --- /dev/null +++ b/docshell/test/browser/file_click_link_within_view_source.html @@ -0,0 +1,6 @@ +<html> +<head> <meta charset="utf-8"> </head> + <body> + <a id="testlink" href="dummy_page.html">clickme</a> + </body> +</html> diff --git a/docshell/test/browser/file_cross_process_csp_inheritance.html b/docshell/test/browser/file_cross_process_csp_inheritance.html new file mode 100644 index 0000000000..d87761a609 --- /dev/null +++ b/docshell/test/browser/file_cross_process_csp_inheritance.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="UTF-8"> + <title>Test CSP inheritance if load happens in same and different process</title> + <meta http-equiv="Content-Security-Policy" content="script-src 'none'"> +</head> +<body> + <a href="data:text/html,<html>test-same-diff-process-csp-inhertiance</html>" id="testLink" target="_blank" rel="noopener">click to test same/diff process CSP inheritance</a> +</body> +</html> diff --git a/docshell/test/browser/file_csp_sandbox_no_script_js_uri.html b/docshell/test/browser/file_csp_sandbox_no_script_js_uri.html new file mode 100644 index 0000000000..49341f7481 --- /dev/null +++ b/docshell/test/browser/file_csp_sandbox_no_script_js_uri.html @@ -0,0 +1,11 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Test Javascript URI with no script</title> +</head> +<body> +<noscript>no scripts allowed here</noscript> +<a href="javascript:alert(`origin=${origin} location=${location}`)" target="_parent">click me</a> +</body> +</html> diff --git a/docshell/test/browser/file_csp_sandbox_no_script_js_uri.html^headers^ b/docshell/test/browser/file_csp_sandbox_no_script_js_uri.html^headers^ new file mode 100644 index 0000000000..461f7f99ce --- /dev/null +++ b/docshell/test/browser/file_csp_sandbox_no_script_js_uri.html^headers^ @@ -0,0 +1 @@ +Content-Security-Policy: sandbox allow-same-origin allow-top-navigation; diff --git a/docshell/test/browser/file_csp_uir.html b/docshell/test/browser/file_csp_uir.html new file mode 100644 index 0000000000..be60f41a80 --- /dev/null +++ b/docshell/test/browser/file_csp_uir.html @@ -0,0 +1,11 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1542858 - Test CSP upgrade-insecure-requests</title> + <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> +</head> +<body> + <a id="testlink" href="file_csp_uir_dummy.html">testlink</a> +</body> +</html> diff --git a/docshell/test/browser/file_csp_uir_dummy.html b/docshell/test/browser/file_csp_uir_dummy.html new file mode 100644 index 0000000000..f0ab6775c0 --- /dev/null +++ b/docshell/test/browser/file_csp_uir_dummy.html @@ -0,0 +1 @@ +<html><body>foo</body></html> diff --git a/docshell/test/browser/file_data_load_inherit_csp.html b/docshell/test/browser/file_data_load_inherit_csp.html new file mode 100644 index 0000000000..1efe738e4c --- /dev/null +++ b/docshell/test/browser/file_data_load_inherit_csp.html @@ -0,0 +1,11 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Bug 1358009 - Inherit CSP into data URI</title> + <meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline'"> +</head> +<body> + <a id="testlink">testlink</a> +</body> +</html> diff --git a/docshell/test/browser/file_multiple_pushState.html b/docshell/test/browser/file_multiple_pushState.html new file mode 100644 index 0000000000..6592f3f53f --- /dev/null +++ b/docshell/test/browser/file_multiple_pushState.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>Test multiple calls to history.pushState</title> + </head> + <body> + <h1>Ohai</h1> + </body> + <script type="text/javascript"> + window.history.pushState({}, "", "/bar/ABC?key=baz"); + let data = new Array(100000).join("a"); + window.history.pushState({ data }, "", "/bar/ABC/DEF?key=baz"); + // Test also Gecko specific state object size limit. + try { + let largeData = new Array(20000000).join("a"); + window.history.pushState({ largeData }, "", "/bar/ABC/DEF/GHI?key=baz"); + } catch (ex) {} + </script> +</html> diff --git a/docshell/test/browser/file_onbeforeunload_0.html b/docshell/test/browser/file_onbeforeunload_0.html new file mode 100644 index 0000000000..7d9acf057d --- /dev/null +++ b/docshell/test/browser/file_onbeforeunload_0.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> +</head> +<body> + <iframe src="http://example.com/browser/docshell/test/browser/file_onbeforeunload_1.html"></iframe> +</body> +</html> diff --git a/docshell/test/browser/file_onbeforeunload_1.html b/docshell/test/browser/file_onbeforeunload_1.html new file mode 100644 index 0000000000..edd27783e4 --- /dev/null +++ b/docshell/test/browser/file_onbeforeunload_1.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> +</head> +<body> + <iframe src="http://mochi.test:8888/browser/docshell/test/browser/file_onbeforeunload_2.html"></iframe> +</body> +</html> diff --git a/docshell/test/browser/file_onbeforeunload_2.html b/docshell/test/browser/file_onbeforeunload_2.html new file mode 100644 index 0000000000..a52a4ace5c --- /dev/null +++ b/docshell/test/browser/file_onbeforeunload_2.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> +</head> +<body> + <iframe src="http://example.com/browser/docshell/test/browser/file_onbeforeunload_3.html"></iframe> +</body> +</html> + diff --git a/docshell/test/browser/file_onbeforeunload_3.html b/docshell/test/browser/file_onbeforeunload_3.html new file mode 100644 index 0000000000..9914f0cd85 --- /dev/null +++ b/docshell/test/browser/file_onbeforeunload_3.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> +</head> +<body> +</body> +</html> + diff --git a/docshell/test/browser/file_open_about_blank.html b/docshell/test/browser/file_open_about_blank.html new file mode 100644 index 0000000000..134384e2f7 --- /dev/null +++ b/docshell/test/browser/file_open_about_blank.html @@ -0,0 +1,2 @@ +<!doctype html> +<button id="open" onclick="window.open('')">Open child window</button> diff --git a/docshell/test/browser/file_slow_load.sjs b/docshell/test/browser/file_slow_load.sjs new file mode 100644 index 0000000000..4c6dd6d5b9 --- /dev/null +++ b/docshell/test/browser/file_slow_load.sjs @@ -0,0 +1,8 @@ +"use strict"; + +function handleRequest(request, response) { + response.processAsync(); + response.setHeader("Content-Type", "text/html"); + response.write("<!doctype html>Loading... "); + // We don't block on this, so it's fine to never finish the response. +} diff --git a/docshell/test/browser/frame-head.js b/docshell/test/browser/frame-head.js new file mode 100644 index 0000000000..6574386b6a --- /dev/null +++ b/docshell/test/browser/frame-head.js @@ -0,0 +1,111 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* eslint-env mozilla/frame-script */ + +// Functions that are automatically loaded as frame scripts for +// timeline tests. + +const { setTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +// Functions that look like mochitest functions but forward to the +// browser process. + +this.ok = function(value, message) { + sendAsyncMessage("browser:test:ok", { + value: !!value, + message, + }); +}; + +this.is = function(v1, v2, message) { + ok(v1 == v2, message); +}; + +this.info = function(message) { + sendAsyncMessage("browser:test:info", { message }); +}; + +this.finish = function() { + sendAsyncMessage("browser:test:finish"); +}; + +/* Start a task that runs some timeline tests in the ordinary way. + * + * @param array tests + * The tests to run. This is an array where each element + * is of the form { desc, searchFor, setup, check }. + * + * desc is the test description, a string. + * searchFor is a string or a function + * If a string, then when a marker with this name is + * found, marker-reading is stopped. + * If a function, then the accumulated marker array is + * passed to it, and marker reading stops when it returns + * true. + * setup is a function that takes the docshell as an argument. + * It should start the test. + * check is a function that takes an array of markers + * as an argument and checks the results of the test. + */ +this.timelineContentTest = function(tests) { + (async function() { + let docShell = content.docShell; + + info("Start recording"); + docShell.recordProfileTimelineMarkers = true; + + for (let { desc, searchFor, setup, check } of tests) { + info("Running test: " + desc); + + info("Flushing the previous markers if any"); + docShell.popProfileTimelineMarkers(); + + info("Running the test setup function"); + let onMarkers = timelineWaitForMarkers(docShell, searchFor); + setup(docShell); + info("Waiting for new markers on the docShell"); + let markers = await onMarkers; + + // Cycle collection markers are non-deterministic, and none of these tests + // expect them to show up. + markers = markers.filter(m => !m.name.includes("nsCycleCollector")); + + info("Running the test check function"); + check(markers); + } + + info("Stop recording"); + docShell.recordProfileTimelineMarkers = false; + finish(); + })(); +}; + +function timelineWaitForMarkers(docshell, searchFor) { + if (typeof searchFor == "string") { + let searchForString = searchFor; + let f = function(markers) { + return markers.some(m => m.name == searchForString); + }; + searchFor = f; + } + + return new Promise(function(resolve, reject) { + let waitIterationCount = 0; + let maxWaitIterationCount = 10; // Wait for 2sec maximum + let markers = []; + + setTimeout(function timeoutHandler() { + let newMarkers = docshell.popProfileTimelineMarkers(); + markers = [...markers, ...newMarkers]; + if (searchFor(markers) || waitIterationCount > maxWaitIterationCount) { + resolve(markers); + } else { + setTimeout(timeoutHandler, 200); + waitIterationCount++; + } + }, 200); + }); +} diff --git a/docshell/test/browser/head.js b/docshell/test/browser/head.js new file mode 100644 index 0000000000..dd6d9242b9 --- /dev/null +++ b/docshell/test/browser/head.js @@ -0,0 +1,258 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* Helper function for timeline tests. Returns an async task that is + * suitable for use as a particular timeline test. + * @param string frameScriptName + * Base name of the frame script file. + * @param string url + * URL to load. + */ +function makeTimelineTest(frameScriptName, url) { + info("in timelineTest"); + return async function() { + info("in in timelineTest"); + waitForExplicitFinish(); + + await timelineTestOpenUrl(url); + + const here = "chrome://mochitests/content/browser/docshell/test/browser/"; + + let mm = gBrowser.selectedBrowser.messageManager; + mm.loadFrameScript(here + "frame-head.js", false); + mm.loadFrameScript(here + frameScriptName, false); + + // Set up some listeners so that timeline tests running in the + // content process can forward their results to the main process. + mm.addMessageListener("browser:test:ok", function(message) { + ok(message.data.value, message.data.message); + }); + mm.addMessageListener("browser:test:info", function(message) { + info(message.data.message); + }); + mm.addMessageListener("browser:test:finish", function(ignore) { + gBrowser.removeCurrentTab(); + finish(); + }); + }; +} + +/* Open a URL for a timeline test. */ +function timelineTestOpenUrl(url) { + window.focus(); + + let tabSwitchPromise = new Promise((resolve, reject) => { + window.gBrowser.addEventListener( + "TabSwitchDone", + function() { + resolve(); + }, + { once: true } + ); + }); + + let loadPromise = new Promise(function(resolve, reject) { + let browser = window.gBrowser; + let tab = (browser.selectedTab = BrowserTestUtils.addTab(browser, url)); + let linkedBrowser = tab.linkedBrowser; + + BrowserTestUtils.browserLoaded(linkedBrowser).then(() => resolve(tab)); + }); + + return Promise.all([tabSwitchPromise, loadPromise]).then(([_, tab]) => tab); +} + +/** + * Helper function for encoding override tests, loads URL, runs check1, + * forces encoding detection, runs check2. + */ +function runCharsetTest(url, check1, check2) { + waitForExplicitFinish(); + + BrowserTestUtils.openNewForegroundTab(gBrowser, url, true).then(afterOpen); + + function afterOpen() { + BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then( + afterChangeCharset + ); + + SpecialPowers.spawn(gBrowser.selectedBrowser, [], check1).then(() => { + BrowserForceEncodingDetection(); + }); + } + + function afterChangeCharset() { + SpecialPowers.spawn(gBrowser.selectedBrowser, [], check2).then(() => { + gBrowser.removeCurrentTab(); + finish(); + }); + } +} + +/** + * Helper function for charset tests. It loads |url| in a new tab, + * runs |check|. + */ +function runCharsetCheck(url, check) { + waitForExplicitFinish(); + + BrowserTestUtils.openNewForegroundTab(gBrowser, url, true).then(afterOpen); + + function afterOpen() { + SpecialPowers.spawn(gBrowser.selectedBrowser, [], check).then(() => { + gBrowser.removeCurrentTab(); + finish(); + }); + } +} + +async function pushState(url, frameId) { + info( + `Doing a pushState, expecting to load ${url} ${ + frameId ? "in an iframe" : "" + }` + ); + let browser = gBrowser.selectedBrowser; + let bc = browser.browsingContext; + if (frameId) { + bc = await SpecialPowers.spawn(bc, [frameId], function(id) { + return content.document.getElementById(id).browsingContext; + }); + } + let loaded = BrowserTestUtils.waitForLocationChange(gBrowser, url); + await SpecialPowers.spawn(bc, [url], function(url) { + content.history.pushState({}, "", url); + }); + await loaded; + info(`Loaded ${url} ${frameId ? "in an iframe" : ""}`); +} + +async function loadURI(url) { + info(`Doing a top-level loadURI, expecting to load ${url}`); + let browser = gBrowser.selectedBrowser; + let loaded = BrowserTestUtils.browserLoaded(browser, false, url); + BrowserTestUtils.loadURI(browser, url); + await loaded; + info(`Loaded ${url}`); +} + +async function followLink(url, frameId) { + info( + `Creating and following a link to ${url} ${frameId ? "in an iframe" : ""}` + ); + let browser = gBrowser.selectedBrowser; + let bc = browser.browsingContext; + if (frameId) { + bc = await SpecialPowers.spawn(bc, [frameId], function(id) { + return content.document.getElementById(id).browsingContext; + }); + } + let loaded = BrowserTestUtils.browserLoaded(browser, !!frameId, url); + await SpecialPowers.spawn(bc, [url], function(url) { + let a = content.document.createElement("a"); + a.href = url; + content.document.body.appendChild(a); + a.click(); + }); + await loaded; + info(`Loaded ${url} ${frameId ? "in an iframe" : ""}`); +} + +async function goForward(url, useFrame = false) { + info( + `Clicking the forward button, expecting to load ${url} ${ + useFrame ? "in an iframe" : "" + }` + ); + let loaded = BrowserTestUtils.waitForLocationChange(gBrowser, url); + let forwardButton = document.getElementById("forward-button"); + forwardButton.click(); + await loaded; + info(`Loaded ${url} ${useFrame ? "in an iframe" : ""}`); +} + +async function goBack(url, useFrame = false) { + info( + `Clicking the back button, expecting to load ${url} ${ + useFrame ? "in an iframe" : "" + }` + ); + let loaded = BrowserTestUtils.waitForLocationChange(gBrowser, url); + let backButton = document.getElementById("back-button"); + backButton.click(); + await loaded; + info(`Loaded ${url} ${useFrame ? "in an iframe" : ""}`); +} + +function assertBackForwardState(canGoBack, canGoForward) { + let backButton = document.getElementById("back-button"); + let forwardButton = document.getElementById("forward-button"); + + is( + backButton.disabled, + !canGoBack, + `${gBrowser.currentURI.spec}: back button is ${ + canGoBack ? "not" : "" + } disabled` + ); + is( + forwardButton.disabled, + !canGoForward, + `${gBrowser.currentURI.spec}: forward button is ${ + canGoForward ? "not" : "" + } disabled` + ); +} + +class SHListener { + static NewEntry = 0; + static Reload = 1; + static GotoIndex = 2; + static Purge = 3; + static ReplaceEntry = 4; + static async waitForHistory(history, event) { + return new Promise(resolve => { + let listener = { + OnHistoryNewEntry: () => {}, + OnHistoryReload: () => { + return true; + }, + OnHistoryGotoIndex: () => {}, + OnHistoryPurge: () => {}, + OnHistoryReplaceEntry: () => {}, + + QueryInterface: ChromeUtils.generateQI([ + "nsISHistoryListener", + "nsISupportsWeakReference", + ]), + }; + + function finish() { + history.removeSHistoryListener(listener); + resolve(); + } + switch (event) { + case this.NewEntry: + listener.OnHistoryNewEntry = finish; + break; + case this.Reload: + listener.OnHistoryReload = () => { + finish(); + return true; + }; + break; + case this.GotoIndex: + listener.OnHistoryGotoIndex = finish; + break; + case this.Purge: + listener.OnHistoryPurge = finish; + break; + case this.ReplaceEntry: + listener.OnHistoryReplaceEntry = finish; + break; + } + + history.addSHistoryListener(listener); + }); + } +} diff --git a/docshell/test/browser/head_browser_onbeforeunload.js b/docshell/test/browser/head_browser_onbeforeunload.js new file mode 100644 index 0000000000..e037822cdb --- /dev/null +++ b/docshell/test/browser/head_browser_onbeforeunload.js @@ -0,0 +1,271 @@ +"use strict"; + +const BASE_URL = "http://mochi.test:8888/browser/docshell/test/browser/"; + +const TEST_PAGE = BASE_URL + "file_onbeforeunload_0.html"; + +const CONTENT_PROMPT_SUBDIALOG = Services.prefs.getBoolPref( + "prompts.contentPromptSubDialog", + false +); + +const { PromptTestUtils } = ChromeUtils.import( + "resource://testing-common/PromptTestUtils.jsm" +); + +async function withTabModalPromptCount(expected, task) { + const DIALOG_TOPIC = CONTENT_PROMPT_SUBDIALOG + ? "common-dialog-loaded" + : "tabmodal-dialog-loaded"; + + let count = 0; + function observer() { + count++; + } + + Services.obs.addObserver(observer, DIALOG_TOPIC); + try { + return await task(); + } finally { + Services.obs.removeObserver(observer, DIALOG_TOPIC); + is(count, expected, "Should see expected number of tab modal prompts"); + } +} + +function promiseAllowUnloadPrompt(browser, allowNavigation) { + return PromptTestUtils.handleNextPrompt( + browser, + { modalType: Services.prompt.MODAL_TYPE_CONTENT, promptType: "confirmEx" }, + { buttonNumClick: allowNavigation ? 0 : 1 } + ); +} + +// Maintain a pool of background tabs with our test document loaded so +// we don't have to wait for a load prior to each test step (potentially +// tearing down and recreating content processes in the process). +const TabPool = { + poolSize: 5, + + pendingCount: 0, + + readyTabs: [], + + readyPromise: null, + resolveReadyPromise: null, + + spawnTabs() { + while (this.pendingCount + this.readyTabs.length < this.poolSize) { + this.pendingCount++; + let tab = BrowserTestUtils.addTab(gBrowser, TEST_PAGE); + BrowserTestUtils.browserLoaded(tab.linkedBrowser).then(() => { + this.readyTabs.push(tab); + this.pendingCount--; + + if (this.resolveReadyPromise) { + this.readyPromise = null; + this.resolveReadyPromise(); + this.resolveReadyPromise = null; + } + + this.spawnTabs(); + }); + } + }, + + getReadyPromise() { + if (!this.readyPromise) { + this.readyPromise = new Promise(resolve => { + this.resolveReadyPromise = resolve; + }); + } + return this.readyPromise; + }, + + async getTab() { + while (!this.readyTabs.length) { + this.spawnTabs(); + await this.getReadyPromise(); + } + + let tab = this.readyTabs.shift(); + this.spawnTabs(); + + gBrowser.selectedTab = tab; + return tab; + }, + + async cleanup() { + this.poolSize = 0; + + while (this.pendingCount) { + await this.getReadyPromise(); + } + + while (this.readyTabs.length) { + await BrowserTestUtils.removeTab(this.readyTabs.shift()); + } + }, +}; + +const ACTIONS = { + NONE: 0, + LISTEN_AND_ALLOW: 1, + LISTEN_AND_BLOCK: 2, +}; + +const ACTION_NAMES = new Map(Object.entries(ACTIONS).map(([k, v]) => [v, k])); + +function* generatePermutations(depth) { + if (depth == 0) { + yield []; + return; + } + for (let subActions of generatePermutations(depth - 1)) { + for (let action of Object.values(ACTIONS)) { + yield [action, ...subActions]; + } + } +} + +const PERMUTATIONS = Array.from(generatePermutations(4)); + +const FRAMES = [ + { process: 0 }, + { process: SpecialPowers.useRemoteSubframes ? 1 : 0 }, + { process: 0 }, + { process: SpecialPowers.useRemoteSubframes ? 1 : 0 }, +]; + +function addListener(bc, block) { + return SpecialPowers.spawn(bc, [block], block => { + return new Promise(resolve => { + function onbeforeunload(event) { + if (block) { + event.preventDefault(); + } + resolve({ event: "beforeunload" }); + } + content.addEventListener("beforeunload", onbeforeunload, { once: true }); + content.unlisten = () => { + content.removeEventListener("beforeunload", onbeforeunload); + }; + + content.addEventListener( + "unload", + () => { + resolve({ event: "unload" }); + }, + { once: true } + ); + }); + }); +} + +function descendants(bc) { + if (bc) { + return [bc, ...descendants(bc.children[0])]; + } + return []; +} + +async function addListeners(frames, actions, startIdx) { + let process = startIdx >= 0 ? FRAMES[startIdx].process : -1; + + let roundTripPromises = []; + + let expectNestedEventLoop = false; + let numBlockers = 0; + let unloadPromises = []; + let beforeUnloadPromises = []; + + for (let [i, frame] of frames.entries()) { + let action = actions[i]; + if (action === ACTIONS.NONE) { + continue; + } + + let block = action === ACTIONS.LISTEN_AND_BLOCK; + let promise = addListener(frame, block); + if (startIdx <= i) { + if (block || FRAMES[i].process !== process) { + expectNestedEventLoop = true; + } + beforeUnloadPromises.push(promise); + numBlockers += block; + } else { + unloadPromises.push(promise); + } + + roundTripPromises.push(SpecialPowers.spawn(frame, [], () => {})); + } + + // Wait for round trip messages to any processes with event listeners to + // return so we're sure that all listeners are registered and their state + // flags are propagated before we continue. + await Promise.all(roundTripPromises); + + return { + expectNestedEventLoop, + expectPrompt: !!numBlockers, + unloadPromises, + beforeUnloadPromises, + }; +} + +async function doTest(actions, startIdx, navigate) { + let tab = await TabPool.getTab(); + let browser = tab.linkedBrowser; + + let frames = descendants(browser.browsingContext); + let expected = await addListeners(frames, actions, startIdx); + + let awaitingPrompt = false; + let promptPromise; + if (expected.expectPrompt) { + awaitingPrompt = true; + promptPromise = promiseAllowUnloadPrompt(browser, false).then(() => { + awaitingPrompt = false; + }); + } + + let promptCount = expected.expectPrompt ? 1 : 0; + await withTabModalPromptCount(promptCount, async () => { + await navigate(tab, frames).then(result => { + ok( + !awaitingPrompt, + "Navigation should not complete while we're still expecting a prompt" + ); + + is( + result.eventLoopSpun, + expected.expectNestedEventLoop, + "Should have nested event loop?" + ); + }); + + for (let result of await Promise.all(expected.beforeUnloadPromises)) { + is( + result.event, + "beforeunload", + "Should have seen beforeunload event before unload" + ); + } + await promptPromise; + + await Promise.all( + frames.map(frame => + SpecialPowers.spawn(frame, [], () => { + if (content.unlisten) { + content.unlisten(); + } + }).catch(() => {}) + ) + ); + + await BrowserTestUtils.removeTab(tab); + }); + + for (let result of await Promise.all(expected.unloadPromises)) { + is(result.event, "unload", "Should have seen unload event"); + } +} diff --git a/docshell/test/browser/onload_message.html b/docshell/test/browser/onload_message.html new file mode 100644 index 0000000000..23f6e37396 --- /dev/null +++ b/docshell/test/browser/onload_message.html @@ -0,0 +1,25 @@ +<html> + <head> + <meta charset="utf-8"> + <script> + addEventListener("load", function() { + // This file is used in couple of different tests. + if (opener) { + opener.postMessage("load", "*"); + } else { + var bc = new BroadcastChannel("browser_browsingContext"); + bc.onmessage = function(event) { + if (event.data == "back") { + bc.close(); + history.back(); + } + }; + bc.postMessage({event: "load", framesLength: frames.length }); + } + }); + </script> + </head> + <body> + This file posts a message containing "load" to opener or BroadcastChannel on load completion. + </body> +</html> diff --git a/docshell/test/browser/onpageshow_message.html b/docshell/test/browser/onpageshow_message.html new file mode 100644 index 0000000000..105c06f8db --- /dev/null +++ b/docshell/test/browser/onpageshow_message.html @@ -0,0 +1,41 @@ +<html> + <head> + <meta charset="utf-8"> + <script> + addEventListener("pageshow", function() { + var bc = new BroadcastChannel("browser_browsingContext"); + function frameData() { + var win = SpecialPowers.wrap(frames[0]); + bc.postMessage( + { framesLength: frames.length, + browsingContextId: win.docShell.browsingContext.id, + outerWindowId: win.docShell.outerWindowID + }); + } + + bc.onmessage = function(event) { + if (event.data == "createiframe") { + let frame = document.createElement("iframe"); + frame.src = "dummy_page.html"; + document.body.appendChild(frame); + frame.onload = frameData; + } else if (event.data == "nextpage") { + bc.close(); + location.href = "onload_message.html"; + } else if (event.data == "queryframes") { + frameData(); + } else if (event.data == "close") { + bc.postMessage("closed"); + bc.close(); + window.close(); + } + } + bc.postMessage("pageshow"); + }); + </script> + </head> + <body> + This file posts a message containing "pageshow" to a BroadcastChannel and + keep the channel open for commands. + </body> +</html> diff --git a/docshell/test/browser/overlink_test.html b/docshell/test/browser/overlink_test.html new file mode 100644 index 0000000000..5efd689311 --- /dev/null +++ b/docshell/test/browser/overlink_test.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<html> + <head> <meta charset="utf-8"> </head> + <body> + <a id="link" href="https://user:password@example.com">Link</a> + </body> +</html> diff --git a/docshell/test/browser/print_postdata.sjs b/docshell/test/browser/print_postdata.sjs new file mode 100644 index 0000000000..0e3ef38419 --- /dev/null +++ b/docshell/test/browser/print_postdata.sjs @@ -0,0 +1,25 @@ +const CC = Components.Constructor; +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); + +function handleRequest(request, response) { + response.setHeader("Content-Type", "text/plain", false); + if (request.method == "GET") { + response.write(request.queryString); + } else { + var body = new BinaryInputStream(request.bodyInputStream); + + var avail; + var bytes = []; + + while ((avail = body.available()) > 0) { + Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } + + var data = String.fromCharCode.apply(null, bytes); + response.bodyOutputStream.write(data, data.length); + } +} diff --git a/docshell/test/browser/redirect_to_example.sjs b/docshell/test/browser/redirect_to_example.sjs new file mode 100644 index 0000000000..283e4793db --- /dev/null +++ b/docshell/test/browser/redirect_to_example.sjs @@ -0,0 +1,5 @@ +function handleRequest(request, response) { + response.setStatusLine(request.httpVersion, 302, "Moved Permanently"); + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + response.setHeader("Location", "http://example"); +} diff --git a/docshell/test/browser/test-form_sjis.html b/docshell/test/browser/test-form_sjis.html new file mode 100644 index 0000000000..91c375deef --- /dev/null +++ b/docshell/test/browser/test-form_sjis.html @@ -0,0 +1,24 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/REC-html401-19991224/strict.dtd"> +<html> + <head> + <meta http-equiv="content-type" content="text/html; charset=windows-1251"> + <title>Shift_JIS in body and text area</title> + </head> + <body> + <h1>Incorrect meta charset</h1> + <h2>This page is encoded in Shift_JIS, but has an incorrect meta charset + claiming that it is windows-1251</h2> + <p id="testpar">jR[h́AׂĂ̕ɌŗL̔ԍt^܂</p> + <form> + <p> + <textarea id="testtextarea" rows=6 cols=60>jR[h́AׂĂ̕ɌŗL̔ԍt^܂</textarea> + <input id="testinput" type="text" size=60 value="jR[h́AׂĂ̕ɌŗL̔ԍt^܂"> + </p> + </form> + <h2>Expected text on load:</h2> + <p>ѓ†ѓjѓRЃ[ѓh‚НЃA‚·‚Ч‚Д‚М•¶Ћљ‚ЙЊЕ—L‚М”ФЌ†‚р•t—^‚µ‚Ь‚·</p> + <h2>Expected text on resetting the encoding to Shift_JIS:</h2> + <p>ユニコードは、すべての文字に固有の番号を付与します</p> + </body> +</html> diff --git a/docshell/test/browser/timelineMarkers-04.html b/docshell/test/browser/timelineMarkers-04.html new file mode 100644 index 0000000000..ff2f429d62 --- /dev/null +++ b/docshell/test/browser/timelineMarkers-04.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"></meta> + <title>markers test</title> +</head> +<body> + + <p>Test page</p> + + <script> + function do_xhr() { + const theURL = "timelineMarkers-04.html"; + + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function() { + dump("ReadyState = " + xhr.readyState + "\n"); + }; + xhr.open("get", theURL, true); + xhr.send(); + } + + window.addEventListener("dog", do_xhr, true); + + function do_promise() { + new Promise(function(resolve, reject) { + console.time("Bob"); + window.setTimeout(function() { + resolve(23); + }, 10); + }).then(function(val) { + console.timeEnd("Bob"); + }); + } + + window.addEventListener("promisetest", do_promise, true); + + var globalResolver; + function do_promise_script() { + new Promise(function(resolve, reject) { + console.time("Bob"); + globalResolver = resolve; + // eslint-disable-next-line no-implied-eval + window.setTimeout("globalResolver(23)", 10); + }).then(function(val) { + console.timeEnd("Bob"); + }); + } + + window.addEventListener("promisescript", do_promise_script, true); + + </script> + +</body> +</html> + diff --git a/docshell/test/chrome/112564_nocache.html b/docshell/test/chrome/112564_nocache.html new file mode 100644 index 0000000000..29fb990b86 --- /dev/null +++ b/docshell/test/chrome/112564_nocache.html @@ -0,0 +1,10 @@ +<html> +<head> +<title>test1</title> +</head> +<body> +<p> +This document will be sent with a no-cache cache-control header. When sent over a secure connection, it should not be stored in bfcache. +</p> +</body> +</html> diff --git a/docshell/test/chrome/112564_nocache.html^headers^ b/docshell/test/chrome/112564_nocache.html^headers^ new file mode 100644 index 0000000000..c829a41ae9 --- /dev/null +++ b/docshell/test/chrome/112564_nocache.html^headers^ @@ -0,0 +1 @@ +Cache-control: no-cache diff --git a/docshell/test/chrome/215405_nocache.html b/docshell/test/chrome/215405_nocache.html new file mode 100644 index 0000000000..c7d48c4eba --- /dev/null +++ b/docshell/test/chrome/215405_nocache.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html style="height: 100%"> +<head> + <title>test1</title> +</head> +<body style="height: 100%"> + <input type="text" id="inp" value=""> + </input> + <div style="height: 50%">Some text</div> + <div style="height: 50%">Some text</div> + <div style="height: 50%">Some text</div> + <div style="height: 50%; width: 300%">Some more text</div> +</body> +</html> diff --git a/docshell/test/chrome/215405_nocache.html^headers^ b/docshell/test/chrome/215405_nocache.html^headers^ new file mode 100644 index 0000000000..c829a41ae9 --- /dev/null +++ b/docshell/test/chrome/215405_nocache.html^headers^ @@ -0,0 +1 @@ +Cache-control: no-cache diff --git a/docshell/test/chrome/215405_nostore.html b/docshell/test/chrome/215405_nostore.html new file mode 100644 index 0000000000..4f5bd0f4f0 --- /dev/null +++ b/docshell/test/chrome/215405_nostore.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html style="height: 100%"> +<head> + <title>test1</title> +</head> +<body style="height: 100%"> + <input type="text" id="inp" value=""> + </input> + <div style="height: 50%">Some text</div> + <div style="height: 50%">Some text</div> + <div style="height: 50%">Some text</div> + <div style="height: 50%; width: 350%">Some more text</div> +</body> +</html> diff --git a/docshell/test/chrome/215405_nostore.html^headers^ b/docshell/test/chrome/215405_nostore.html^headers^ new file mode 100644 index 0000000000..59ba296103 --- /dev/null +++ b/docshell/test/chrome/215405_nostore.html^headers^ @@ -0,0 +1 @@ +Cache-control: no-store diff --git a/docshell/test/chrome/582176_dummy.html b/docshell/test/chrome/582176_dummy.html new file mode 100644 index 0000000000..3b18e512db --- /dev/null +++ b/docshell/test/chrome/582176_dummy.html @@ -0,0 +1 @@ +hello world diff --git a/docshell/test/chrome/582176_xml.xml b/docshell/test/chrome/582176_xml.xml new file mode 100644 index 0000000000..d3dd576dfe --- /dev/null +++ b/docshell/test/chrome/582176_xml.xml @@ -0,0 +1,2 @@ +<?xml-stylesheet type="text/xsl" href="582176_xslt.xsl"?> +<out/> diff --git a/docshell/test/chrome/582176_xslt.xsl b/docshell/test/chrome/582176_xslt.xsl new file mode 100644 index 0000000000..5957416899 --- /dev/null +++ b/docshell/test/chrome/582176_xslt.xsl @@ -0,0 +1,8 @@ +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:template match="out"> + <html> + <head><title>XSLT result doc</title></head> + <body><p>xslt result</p></body> + </html> + </xsl:template> +</xsl:stylesheet> diff --git a/docshell/test/chrome/662200a.html b/docshell/test/chrome/662200a.html new file mode 100644 index 0000000000..0b9ead6f3e --- /dev/null +++ b/docshell/test/chrome/662200a.html @@ -0,0 +1,8 @@ +<html> + <head> + <title>A</title> + </head> + <body> + <a id="link" href="662200b.html">Next</a> + </body> +</html> diff --git a/docshell/test/chrome/662200b.html b/docshell/test/chrome/662200b.html new file mode 100644 index 0000000000..91e6b971d6 --- /dev/null +++ b/docshell/test/chrome/662200b.html @@ -0,0 +1,8 @@ +<html> + <head> + <title>B</title> + </head> + <body> + <a id="link" href="662200c.html">Next</a> + </body> +</html> diff --git a/docshell/test/chrome/662200c.html b/docshell/test/chrome/662200c.html new file mode 100644 index 0000000000..bc00e6b14b --- /dev/null +++ b/docshell/test/chrome/662200c.html @@ -0,0 +1,7 @@ +<html> + <head> + <title>C</title> + </head> + <body> + </body> +</html> diff --git a/docshell/test/chrome/89419.html b/docshell/test/chrome/89419.html new file mode 100644 index 0000000000..b36b8d788c --- /dev/null +++ b/docshell/test/chrome/89419.html @@ -0,0 +1,7 @@ +<html> +<head> +<title>Bug 89419</title> +</head> +<body> +<img src="http://mochi.test:8888/tests/docshell/test/chrome/bug89419.sjs"> +</body> diff --git a/docshell/test/chrome/92598_nostore.html b/docshell/test/chrome/92598_nostore.html new file mode 100644 index 0000000000..47bb90441e --- /dev/null +++ b/docshell/test/chrome/92598_nostore.html @@ -0,0 +1,10 @@ +<html> +<head> +<title>test1</title> +</head> +<body> +<p> +This document will be sent with a no-store cache-control header. It should not be stored in bfcache. +</p> +</body> +</html> diff --git a/docshell/test/chrome/92598_nostore.html^headers^ b/docshell/test/chrome/92598_nostore.html^headers^ new file mode 100644 index 0000000000..59ba296103 --- /dev/null +++ b/docshell/test/chrome/92598_nostore.html^headers^ @@ -0,0 +1 @@ +Cache-control: no-store diff --git a/docshell/test/chrome/DocShellHelpers.sys.mjs b/docshell/test/chrome/DocShellHelpers.sys.mjs new file mode 100644 index 0000000000..5f4eee8724 --- /dev/null +++ b/docshell/test/chrome/DocShellHelpers.sys.mjs @@ -0,0 +1,74 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ + +export class DocShellHelpersParent extends JSWindowActorParent { + static eventListener; + + // These static variables should be set when registering the actor + // (currently doPageNavigation in docshell_helpers.js). + static eventsToListenFor; + static observers; + + constructor() { + super(); + } + receiveMessage({ name, data }) { + if (name == "docshell_helpers:event") { + let { event, originalTargetIsHTMLDocument } = data; + + if (this.constructor.eventsToListenFor.includes(event.type)) { + this.constructor.eventListener(event, originalTargetIsHTMLDocument); + } + } else if (name == "docshell_helpers:observe") { + let { topic } = data; + + this.constructor.observers.get(topic).call(); + } + } +} + +export class DocShellHelpersChild extends JSWindowActorChild { + constructor() { + super(); + } + receiveMessage({ name, data }) { + if (name == "docshell_helpers:preventBFCache") { + // Add an RTCPeerConnection to prevent the page from being bfcached. + let win = this.contentWindow; + win.blockBFCache = new win.RTCPeerConnection(); + } + } + handleEvent(event) { + if ( + Document.isInstance(event.originalTarget) && + event.originalTarget.isInitialDocument + ) { + dump(`TEST: ignoring a ${event.type} event for an initial about:blank\n`); + return; + } + + this.sendAsyncMessage("docshell_helpers:event", { + event: { + type: event.type, + persisted: event.persisted, + originalTarget: { + title: event.originalTarget.title, + location: event.originalTarget.location.href, + visibilityState: event.originalTarget.visibilityState, + hidden: event.originalTarget.hidden, + }, + }, + originalTargetIsHTMLDocument: HTMLDocument.isInstance( + event.originalTarget + ), + }); + } + observe(subject, topic) { + if (Window.isInstance(subject) && subject.document.isInitialDocument) { + dump(`TEST: ignoring a topic notification for an initial about:blank\n`); + return; + } + + this.sendAsyncMessage("docshell_helpers:observe", { topic }); + } +} diff --git a/docshell/test/chrome/allowContentRetargeting.sjs b/docshell/test/chrome/allowContentRetargeting.sjs new file mode 100644 index 0000000000..96e467ef68 --- /dev/null +++ b/docshell/test/chrome/allowContentRetargeting.sjs @@ -0,0 +1,7 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function handleRequest(req, resp) { + resp.setHeader("Content-Type", "application/octet-stream", false); + resp.write("hi"); +} diff --git a/docshell/test/chrome/blue.png b/docshell/test/chrome/blue.png Binary files differnew file mode 100644 index 0000000000..8df58f3a5f --- /dev/null +++ b/docshell/test/chrome/blue.png diff --git a/docshell/test/chrome/bug112564_window.xhtml b/docshell/test/chrome/bug112564_window.xhtml new file mode 100644 index 0000000000..b4c7d05dc2 --- /dev/null +++ b/docshell/test/chrome/bug112564_window.xhtml @@ -0,0 +1,86 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="112564Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="onLoad();" + title="112564 test"> + + <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + var gTestsIterator; + + function onLoad() { + gTestsIterator = testsIterator(); + nextTest(); + } + + function nextTest() { + gTestsIterator.next(); + } + + function* testsIterator() { + // Load a secure page with a no-cache header, followed by a simple page. + // no-cache should not interfere with the bfcache in the way no-store + // does. + var test1DocURI = "https://example.com:443/chrome/docshell/test/chrome/112564_nocache.html"; + + doPageNavigation({ + uri: test1DocURI, + eventsToListenFor: ["load", "pageshow"], + expectedEvents: [ { type: "load", + title: "test1" }, + { type: "pageshow", + title: "test1", + persisted: false } ], + onNavComplete: nextTest + }); + yield undefined; + + var test2DocURI = "data:text/html,<html><head><title>test2</title></head>" + + "<body>test2</body></html>"; + + doPageNavigation({ + uri: test2DocURI, + eventsToListenFor: ["load", "pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "test1", + persisted: true }, + { type: "load", + title: "test2" }, + { type: "pageshow", + title: "test2", + persisted: false } ], + onNavComplete: nextTest + }); + yield undefined; + + // Now go back in history. First page has been cached. + // Check persisted property to confirm + doPageNavigation({ + back: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "test2", + persisted: true }, + { type: "pageshow", + title: "test1", + persisted: true } ], + onNavComplete: nextTest + }); + yield undefined; + + finish(); + } + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" remote="true" /> +</window> diff --git a/docshell/test/chrome/bug113934_window.xhtml b/docshell/test/chrome/bug113934_window.xhtml new file mode 100644 index 0000000000..cf6343f309 --- /dev/null +++ b/docshell/test/chrome/bug113934_window.xhtml @@ -0,0 +1,165 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<window title="Mozilla Bug 113934" onload="doTheTest()" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <hbox> + <vbox id="box1"> + </vbox> + <vbox id="box2"> + </vbox> + <spacer flex="1"/> + </hbox> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + /* globals SimpleTest, is, isnot, ok, snapshotWindow, compareSnapshots, + onerror */ + var imports = [ "SimpleTest", "is", "isnot", "ok", "snapshotWindow", + "compareSnapshots", "onerror" ]; + for (var name of imports) { + window[name] = window.arguments[0][name]; + } + + function $(id) { + return document.getElementById(id); + } + + function addBrowser(parent, id, width, height) { + var b = + document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "browser"); + var type = window.location.search.slice(1); + is(type == "chrome" || type == "content", true, "Unexpected type"); + b.setAttribute("type", type); + b.setAttribute("id", id); + b.setAttribute("width", width); + b.setAttribute("height", height); + $(parent).appendChild(b); + } + addBrowser("box1", "f1", 300, 200); + addBrowser("box1", "f2", 300, 200); + addBrowser("box2", "f3", 30, 200); + + /** Test for Bug 113934 **/ + var doc1 = + "data:text/html,<html><body onbeforeunload='document.documentElement.textContent = \"\"' onunload='document.documentElement.textContent = \"\"' onpagehide='document.documentElement.textContent = \"\"'>This is a test</body></html>"; + var doc2 = "data:text/html,<html><head></head><body>This is a second test</body></html>"; + + + $("f1").setAttribute("src", doc1); + $("f2").setAttribute("src", doc2); + $("f3").setAttribute("src", doc2); + + async function doTheTest() { + var s2 = await snapshotWindow($("f2").contentWindow); + // var s3 = await snapshotWindow($("f3").contentWindow); + + // This test is broken - see bug 1090274 + //ok(!compareSnapshots(s2, s3, true)[0], + // "Should look different due to different sizing"); + + function getDOM(id) { + return $(id).contentDocument.documentElement.innerHTML; + } + + var dom1 = getDOM("f1"); + + var dom2 = getDOM("f2"); + $("f2").contentDocument.body.textContent = "Modified the text"; + var dom2star = getDOM("f2"); + isnot(dom2, dom2star, "We changed the DOM!"); + + $("f1").swapDocShells($("f2")); + // now we have doms 2*, 1, 2 in the frames + + is(getDOM("f1"), dom2star, "Shouldn't have changed the DOM on swap"); + is(getDOM("f2"), dom1, "Shouldn't have fired event handlers"); + + // Test for bug 480149 + // The DOMLink* events are dispatched asynchronously, thus I cannot + // just include the <link> element in the initial DOM and swap the + // docshells. Instead, the link element is added now. Then, when the + // first DOMLinkAdded event (which is a result of the actual addition) + // is dispatched, the docshells are swapped and the pageshow and pagehide + // events are tested. Only then, we wait for the DOMLink* events, + // which are a result of swapping the docshells. + var DOMLinkListener = { + _afterFirst: false, + _removedDispatched: false, + _addedDispatched: false, + async handleEvent(aEvent) { + if (!this._afterFirst) { + is(aEvent.type, "DOMLinkAdded"); + + var strs = { "f1": "", "f3" : "" }; + function attachListener(node, type) { + var listener = function(e) { + if (strs[node.id]) strs[node.id] += " "; + strs[node.id] += node.id + ".page" + type; + } + node.addEventListener("page" + type, listener); + + listener.detach = function() { + node.removeEventListener("page" + type, listener); + } + return listener; + } + + var l1 = attachListener($("f1"), "show"); + var l2 = attachListener($("f1"), "hide"); + var l3 = attachListener($("f3"), "show"); + var l4 = attachListener($("f3"), "hide"); + + $("f1").swapDocShells($("f3")); + // now we have DOMs 2, 1, 2* in the frames + + l1.detach(); + l2.detach(); + l3.detach(); + l4.detach(); + + // swapDocShells reflows asynchronously, ensure layout is + // clean so that the viewport of f1 is the right size. + $("f1").getBoundingClientRect(); + var s1_new = await snapshotWindow($("f1").contentWindow); + var [same, first, second] = compareSnapshots(s1_new, s2, true); + ok(same, "Should reflow on swap. Expected " + second + " but got " + first); + + is(strs.f1, "f1.pagehide f1.pageshow"); + is(strs.f3, "f3.pagehide f3.pageshow"); + this._afterFirst = true; + return; + } + if (aEvent.type == "DOMLinkAdded") { + is(this._addedDispatched, false); + this._addedDispatched = true; + } + else { + is(this._removedDispatched, false); + this._removedDispatched = true; + } + + if (this._addedDispatched && this._removedDispatched) { + $("f1").removeEventListener("DOMLinkAdded", this); + $("f1").removeEventListener("DOMLinkRemoved", this); + $("f3").removeEventListener("DOMLinkAdded", this); + $("f3").removeEventListener("DOMLinkRemoved", this); + window.close(); + SimpleTest.finish(); + } + } + }; + + $("f1").addEventListener("DOMLinkAdded", DOMLinkListener); + $("f1").addEventListener("DOMLinkRemoved", DOMLinkListener); + $("f3").addEventListener("DOMLinkAdded", DOMLinkListener); + $("f3").addEventListener("DOMLinkRemoved", DOMLinkListener); + + var linkElement = $("f1").contentDocument.createElement("link"); + linkElement.setAttribute("rel", "alternate"); + linkElement.setAttribute("href", "about:blank"); + $("f1").contentDocument.documentElement.firstChild.appendChild(linkElement); + } + + ]]></script> +</window> diff --git a/docshell/test/chrome/bug215405_window.xhtml b/docshell/test/chrome/bug215405_window.xhtml new file mode 100644 index 0000000000..73366482b7 --- /dev/null +++ b/docshell/test/chrome/bug215405_window.xhtml @@ -0,0 +1,177 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="215405Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="onLoad();" + title="215405 test"> + + <script type="application/javascript"><![CDATA[ + const {BrowserTestUtils} = ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm"); + Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false); + + /* globals SimpleTest, is, isnot, ok */ + var imports = [ "SimpleTest", "is", "isnot", "ok"]; + for (var name of imports) { + window[name] = window.arguments[0][name]; + } + + const text="MOZILLA"; + const nostoreURI = "http://mochi.test:8888/tests/docshell/test/chrome/" + + "215405_nostore.html"; + const nocacheURI = "https://example.com:443/tests/docshell/test/chrome/" + + "215405_nocache.html"; + + var gBrowser; + var gTestsIterator; + var currScrollX = 0; + var currScrollY = 0; + + function finish() { + gBrowser.removeEventListener("pageshow", eventListener, true); + // Work around bug 467960 + let historyPurged; + if (SpecialPowers.Services.appinfo.sessionHistoryInParent) { + let history = gBrowser.browsingContext.sessionHistory; + history.purgeHistory(history.count); + historyPurged = Promise.resolve(); + } else { + historyPurged = SpecialPowers.spawn(gBrowser, [], () => { + let history = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory + .legacySHistory; + history.purgeHistory(history.count); + }); + } + + historyPurged.then(_ => { + window.close(); + window.arguments[0].SimpleTest.finish(); + }); + } + + function onLoad(e) { + gBrowser = document.getElementById("content"); + gBrowser.addEventListener("pageshow", eventListener, true); + + gTestsIterator = testsIterator(); + nextTest(); + } + + function eventListener(event) { + setTimeout(nextTest, 0); + } + + function nextTest() { + gTestsIterator.next(); + } + + function* testsIterator() { + // No-store tests + var testName = "[nostore]"; + + // Load a page with a no-store header + BrowserTestUtils.loadURI(gBrowser, nostoreURI); + yield undefined; + + + // Now that the page has loaded, amend the form contents + var form = gBrowser.contentDocument.getElementById("inp"); + form.value = text; + + // Attempt to scroll the page + var originalXPosition = gBrowser.contentWindow.scrollX; + var originalYPosition = gBrowser.contentWindow.scrollY; + var scrollToX = gBrowser.contentWindow.scrollMaxX; + var scrollToY = gBrowser.contentWindow.scrollMaxY; + gBrowser.contentWindow.scrollBy(scrollToX, scrollToY); + + // Save the scroll position for future comparison + currScrollX = gBrowser.contentWindow.scrollX; + currScrollY = gBrowser.contentWindow.scrollY; + isnot(currScrollX, originalXPosition, + testName + " failed to scroll window horizontally"); + isnot(currScrollY, originalYPosition, + testName + " failed to scroll window vertically"); + + // Load a new document into the browser + var simple = "data:text/html,<html><head><title>test2</title></head>" + + "<body>test2</body></html>"; + BrowserTestUtils.loadURI(gBrowser, simple); + yield undefined; + + + // Now go back in history. First page should not have been cached. + gBrowser.goBack(); + yield undefined; + + + // First uncacheable page will now be reloaded. Check scroll position + // restored, and form contents not + is(gBrowser.contentWindow.scrollX, currScrollX, testName + + " horizontal axis scroll position not correctly restored"); + is(gBrowser.contentWindow.scrollY, currScrollY, testName + + " vertical axis scroll position not correctly restored"); + var formValue = gBrowser.contentDocument.getElementById("inp").value; + isnot(formValue, text, testName + " form value incorrectly restored"); + + + // https no-cache + testName = "[nocache]"; + + // Load a page with a no-cache header. This should not be + // restricted like no-store (bug 567365) + BrowserTestUtils.loadURI(gBrowser, nocacheURI); + yield undefined; + + + // Now that the page has loaded, amend the form contents + form = gBrowser.contentDocument.getElementById("inp"); + form.value = text; + + // Attempt to scroll the page + originalXPosition = gBrowser.contentWindow.scrollX; + originalYPosition = gBrowser.contentWindow.scrollY; + scrollToX = gBrowser.contentWindow.scrollMaxX; + scrollToY = gBrowser.contentWindow.scrollMaxY; + gBrowser.contentWindow.scrollBy(scrollToX, scrollToY); + + // Save the scroll position for future comparison + currScrollX = gBrowser.contentWindow.scrollX; + currScrollY = gBrowser.contentWindow.scrollY; + isnot(currScrollX, originalXPosition, + testName + " failed to scroll window horizontally"); + isnot(currScrollY, originalYPosition, + testName + " failed to scroll window vertically"); + + BrowserTestUtils.loadURI(gBrowser, simple); + yield undefined; + + + // Now go back in history to the cached page. + gBrowser.goBack(); + yield undefined; + + + // First page will now be reloaded. Check scroll position + // and form contents are restored + is(gBrowser.contentWindow.scrollX, currScrollX, testName + + " horizontal axis scroll position not correctly restored"); + is(gBrowser.contentWindow.scrollY, currScrollY, testName + + " vertical axis scroll position not correctly restored"); + formValue = gBrowser.contentDocument.getElementById("inp").value; + is(formValue, text, testName + " form value not correctly restored"); + + Services.prefs.clearUserPref("browser.navigation.requireUserInteraction"); + finish(); + } + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" src="about:blank"/> +</window> diff --git a/docshell/test/chrome/bug293235.html b/docshell/test/chrome/bug293235.html new file mode 100644 index 0000000000..458f88431c --- /dev/null +++ b/docshell/test/chrome/bug293235.html @@ -0,0 +1,13 @@ +<html> + <head> + <title>Bug 293235 page1</title> + <style type="text/css"> + a:visited, a.forcevisited.forcevisited { color: rgb(128, 0, 128); } + a:link, a.forcelink.forcelink { color: rgb(0, 0, 128); } + a:focus { color: rgb(128, 0, 0); } + </style> + </head> + <body> + <a id="link1" href="bug293235_p2.html">This is a test link.</a> + </body> +</html> diff --git a/docshell/test/chrome/bug293235_p2.html b/docshell/test/chrome/bug293235_p2.html new file mode 100644 index 0000000000..2de067b80e --- /dev/null +++ b/docshell/test/chrome/bug293235_p2.html @@ -0,0 +1,8 @@ +<html> + <head> + <title>Bug 293235 page2</title> + </head> + <body> + Nothing to see here, move along. + </body> +</html> diff --git a/docshell/test/chrome/bug293235_window.xhtml b/docshell/test/chrome/bug293235_window.xhtml new file mode 100644 index 0000000000..7d87517824 --- /dev/null +++ b/docshell/test/chrome/bug293235_window.xhtml @@ -0,0 +1,118 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="293235Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="setTimeout(runTests, 0);" + title="bug 293235 test"> + + <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script> + + <script type="application/javascript"><![CDATA[ + var {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); + + // Return the Element object for the specified element id + function $(id) { return TestWindow.getDocument().getElementById(id); } + + //// + // Generator function for test steps for bug 293235: + // A visited link should have the :visited style applied + // to it when displayed on a page which was fetched from + // the bfcache. + // + async function runTests() { + // Register our observer to know when the link lookup is complete. + let testURI = NetUtil.newURI(getHttpUrl("bug293235_p2.html")); + let os = SpecialPowers.Services.obs; + // Load a test page containing a link that should be initially + // blue, per the :link style. + await new Promise(resolve => { + doPageNavigation({ + uri: getHttpUrl("bug293235.html"), + onNavComplete: resolve, + }); + }); + + // Now that we've been notified, we can check our link color. + // Since we can't use getComputedStyle() for this because + // getComputedStyle lies about styles that result from :visited, + // we have to take snapshots. + // First, take two reference snapshots. + var link1 = $("link1"); + link1.className = "forcelink"; + var refLink = await snapshotWindow(TestWindow.getWindow()); + link1.className = "forcevisited"; + var refVisited = await snapshotWindow(TestWindow.getWindow()); + link1.className = ""; + function snapshotsEqual(snap1, snap2) { + return compareSnapshots(snap1, snap2, true)[0]; + } + ok(!snapshotsEqual(refLink, refVisited), "references should not match"); + ok(snapshotsEqual(refLink, await snapshotWindow(TestWindow.getWindow())), + "link should initially be blue"); + + let observedVisit = false, observedPageShow = false; + await new Promise(resolve => { + function maybeResolve() { + ok(true, "maybe run next test? visited: " + observedVisit + " pageShow: " + observedPageShow); + if (observedVisit && observedPageShow) + resolve(); + } + + // Because adding visits is async, we will not be notified immediately. + let visitObserver = { + observe(aSubject, aTopic, aData) + { + if (!testURI.equals(aSubject.QueryInterface(Ci.nsIURI))) { + return; + } + os.removeObserver(this, aTopic); + observedVisit = true; + maybeResolve(); + }, + }; + os.addObserver(visitObserver, "uri-visit-saved"); + // Load the page that the link on the previous page points to. + doPageNavigation({ + uri: getHttpUrl("bug293235_p2.html"), + onNavComplete() { + observedPageShow = true; + maybeResolve(); + } + }); + }) + + // And the nodes get notified after the "uri-visit-saved" topic, so + // we need to execute soon... + await new Promise(SimpleTest.executeSoon); + + // Go back, verify the original page was loaded from the bfcache, + // and verify that the link is now purple, per the + // :visited style. + await new Promise(resolve => { + doPageNavigation({ + back: true, + eventsToListenFor: ["pageshow"], + expectedEvents: [ { type: "pageshow", + persisted: true, + title: "Bug 293235 page1" } ], + onNavComplete: resolve, + }); + }) + + // Now we can test the link color. + ok(snapshotsEqual(refVisited, await snapshotWindow(TestWindow.getWindow())), + "visited link should be purple"); + + // Tell the framework the test is finished. + finish(); + } + + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" src="about:blank"/> +</window> diff --git a/docshell/test/chrome/bug294258_testcase.html b/docshell/test/chrome/bug294258_testcase.html new file mode 100644 index 0000000000..cd80fefd06 --- /dev/null +++ b/docshell/test/chrome/bug294258_testcase.html @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" +"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>Bug 294258 Testcase</title> + <meta http-equiv="Content-Type" content="application/xhtml+xml"/> + <style type="text/css"> + * { + font-family: monospace; + } + </style> + </head> + <body> + <div> + <p> + input type="text": <input id="text" type="text"/> + </p> + <p> + input type="checkbox": <input id="checkbox" type="checkbox"/> + </p> + <p> + input type="file": <input id="file" type="file"/> + </p> + <p> + input type="radio": + <input type="radio" id="radio1" name="radio" value="radio1"/> + <input id="radio2" type="radio" name="radio" value="radio2"/> + </p> + <p> + textarea: <textarea id="textarea" rows="4" cols="80"></textarea> + </p> + <p> + select -> option: <select id="select"> + <option>1</option> + <option>2</option> + <option>3</option> + <option>4</option> + </select> + </p> + </div> + </body> +</html> diff --git a/docshell/test/chrome/bug294258_window.xhtml b/docshell/test/chrome/bug294258_window.xhtml new file mode 100644 index 0000000000..fe47f3e70f --- /dev/null +++ b/docshell/test/chrome/bug294258_window.xhtml @@ -0,0 +1,72 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="294258Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="setTimeout(nextTest, 0);" + title="bug 294258 test"> + + <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + + // Define the generator-iterator for the tests. + var tests = testIterator(); + + //// + // Execute the next test in the generator function. + // + function nextTest() { + tests.next(); + } + + function $(id) { return TestWindow.getDocument().getElementById(id); } + + //// + // Generator function for test steps for bug 294258: + // Form values should be preserved on reload. + // + function* testIterator() + { + // Load a page containing a form. + doPageNavigation( { + uri: getHttpUrl("bug294258_testcase.html"), + onNavComplete: nextTest + } ); + yield undefined; + + // Change the data for each of the form fields, and reload. + $("text").value = "text value"; + $("checkbox").checked = true; + var file = SpecialPowers.Services.dirsvc.get("TmpD", Ci.nsIFile); + file.append("294258_test.file"); + file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666); + let filePath = file.path; + $("file").value = filePath; + $("textarea").value = "textarea value"; + $("radio1").checked = true; + $("select").selectedIndex = 2; + doPageNavigation( { + reload: true, + onNavComplete: nextTest + } ); + yield undefined; + + // Verify that none of the form data has changed. + is($("text").value, "text value", "Text value changed"); + is($("checkbox").checked, true, "Checkbox value changed"); + is($("file").value, filePath, "File value changed"); + is($("textarea").value, "textarea value", "Textarea value changed"); + is($("radio1").checked, true, "Radio value changed"); + is($("select").selectedIndex, 2, "Select value changed"); + + // Tell the framework the test is finished. + finish(); + } + + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" src="about:blank"/> +</window> diff --git a/docshell/test/chrome/bug298622_window.xhtml b/docshell/test/chrome/bug298622_window.xhtml new file mode 100644 index 0000000000..318fb9d1d3 --- /dev/null +++ b/docshell/test/chrome/bug298622_window.xhtml @@ -0,0 +1,135 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="298622Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="setTimeout(runTest, 0);" + title="bug 298622 test"> + + <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src= "docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + // Global variable that holds a reference to the find bar. + var gFindBar; + + //// + // Test for bug 298622: + // Find should work correctly on a page loaded from the + // bfcache. + // + async function runTest() + { + // Make sure bfcache is on. + enableBFCache(true); + + // Load a test page which contains some text to be found. + await promisePageNavigation({ + uri: "data:text/html,<html><head><title>test1</title></head>" + + "<body>find this!</body></html>", + }); + + // Load a second, dummy page, verifying that the original + // page gets stored in the bfcache. + await promisePageNavigation({ + uri: getHttpUrl("generic.html"), + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "test1", + persisted: true }, + { type: "pageshow", + title: "generic page" } ], + }); + + // Make sure we unsuppress painting before continuing + await new Promise(resolve => { + SimpleTest.executeSoon(resolve); + }); + + // Search for some text that's on the second page (but not on + // the first page), and verify that it can be found. + gFindBar = document.getElementById("FindToolbar"); + document.getElementById("cmd_find").doCommand(); + ok(!gFindBar.hidden, "failed to open findbar"); + gFindBar._findField.value = "A generic page"; + gFindBar._find(); + await new Promise(resolve => { + SimpleTest.executeSoon(resolve); + }); + + // Make sure Find bar's internal status is not 'notfound' + isnot(gFindBar._findField.getAttribute("status"), "notfound", + "Findfield status attribute should not have been 'notfound'" + + " after Find"); + + // Make sure the key events above have time to be processed + // before continuing + await promiseTrue(() => + SpecialPowers.spawn(document.getElementById("content"), [], function() { + return content.document.getSelection().toString().toLowerCase() == "a generic page"; + }), 20 + ); + + is(gFindBar._findField.value, "A generic page", + "expected text not present in find input field"); + is(await SpecialPowers.spawn(document.getElementById("content"), [], async function() { + return content.document.getSelection().toString().toLowerCase(); + }), + "a generic page", + "find failed on second page loaded"); + + // Go back to the original page and verify it's loaded from the + // bfcache. + await promisePageNavigation({ + back: true, + eventsToListenFor: ["pageshow"], + expectedEvents: [ { type: "pageshow", + title: "test1", + persisted: true } ], + }); + + // Search for some text that's on the original page (but not + // the dummy page loaded above), and verify that it can + // be found. + gFindBar = document.getElementById("FindToolbar"); + document.getElementById("cmd_find").doCommand(); + ok(!gFindBar.hidden, "failed to open findbar"); + gFindBar._findField.value = "find this"; + gFindBar._find(); + await new Promise(resolve => { + SimpleTest.executeSoon(resolve); + }); + + // Make sure Find bar's internal status is not 'notfound' + isnot(gFindBar._findField.getAttribute("status"), "notfound", + "Findfield status attribute should not have been 'notfound'" + + " after Find"); + + // Make sure the key events above have time to be processed + // before continuing + await promiseTrue(() => + SpecialPowers.spawn(document.getElementById("content"), [], function() { + return content.document.getSelection().toString().toLowerCase() == "find this"; + }), 20 + ); + + is(await SpecialPowers.spawn(document.getElementById("content"), [], async function() { + return content.document.getSelection().toString().toLowerCase(); + }), + "find this", + "find failed on page loaded from bfcache"); + + // Tell the framework the test is finished. + finish(); + } + + ]]></script> + + <commandset> + <command id="cmd_find" + oncommand="document.getElementById('FindToolbar').onFindCommand();"/> + </commandset> + <browser type="content" primary="true" flex="1" id="content" messagemanagergroup="test" remote="true" /> + <findbar id="FindToolbar" browserid="content"/> +</window> diff --git a/docshell/test/chrome/bug301397_1.html b/docshell/test/chrome/bug301397_1.html new file mode 100644 index 0000000000..9943c2efe6 --- /dev/null +++ b/docshell/test/chrome/bug301397_1.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html> +<head> + <title>iframe parent</title> + </head> +<body> + <iframe id="iframe" src="bug301397_2.html"/> + </body> +</html> diff --git a/docshell/test/chrome/bug301397_2.html b/docshell/test/chrome/bug301397_2.html new file mode 100644 index 0000000000..4237107060 --- /dev/null +++ b/docshell/test/chrome/bug301397_2.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<head> + <title>iframe content #1</title> + </head> +<body> + iframe page 1<br/> + <a id="link" href="bug301397_3.html">go to next page</a> + </body> +</html> diff --git a/docshell/test/chrome/bug301397_3.html b/docshell/test/chrome/bug301397_3.html new file mode 100644 index 0000000000..8d36e92461 --- /dev/null +++ b/docshell/test/chrome/bug301397_3.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<head> + <title>iframe content #2</title> + </head> +<body> + iframe page 2<br/> + You made it! + </body> +</html> diff --git a/docshell/test/chrome/bug301397_4.html b/docshell/test/chrome/bug301397_4.html new file mode 100644 index 0000000000..5584a4554a --- /dev/null +++ b/docshell/test/chrome/bug301397_4.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html> +<head> + <title>dummy page, no iframe</title> + </head> +<body> + Just a boring test page, nothing special. + </body> +</html> diff --git a/docshell/test/chrome/bug301397_window.xhtml b/docshell/test/chrome/bug301397_window.xhtml new file mode 100644 index 0000000000..bf0f33c794 --- /dev/null +++ b/docshell/test/chrome/bug301397_window.xhtml @@ -0,0 +1,218 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="301397Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="setTimeout(runTest, 0);" + title="bug 301397 test"> + + <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false); + + //// + // Verifies that the given string exists in the innerHTML of the iframe + // content. + // + async function verifyIframeInnerHtml(string) { + var iframeInnerHtml = await SpecialPowers.spawn(document.getElementById("content"), [string], (string) => + content.document.getElementById("iframe").contentDocument.body.innerHTML); + ok(iframeInnerHtml.includes(string), + "iframe contains wrong document: " + iframeInnerHtml); + } + + //// + // Generator function for test steps for bug 301397: + // The correct page should be displayed in an iframe when + // navigating back and forwards, when the parent page + // occupies multiple spots in the session history. + // + async function runTest() + { + // Make sure the bfcache is enabled. + enableBFCache(8); + + // Load a dummy page. + await promisePageNavigation({ + uri: getHttpUrl("generic.html"), + }); + + // Load a page containing an iframe. + await promisePageNavigation({ + uri: getHttpUrl("bug301397_1.html"), + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "generic page", + persisted: true }, + { type: "pageshow", + title: "iframe content #1", + persisted: false }, // false on initial load + { type: "pageshow", + title: "iframe parent", + persisted: false } ], // false on initial load + }); + + // Click a link in the iframe to cause the iframe to navigate + // to a new page, and wait until the related pagehide/pageshow + // events have occurred. + let waitForLinkNavigation = promisePageEvents({ + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "iframe content #1", + persisted: false }, // false, subframe nav + { type: "pageshow", + title: "iframe content #2", + persisted: false } ], // false on initial load + }); + SpecialPowers.spawn(document.getElementById("content"), [], function() { + let iframe = content.document.getElementById("iframe"); + let link = iframe.contentDocument.getElementById("link"); + let event = iframe.contentDocument.createEvent("MouseEvents"); + event.initMouseEvent("click", true, true, iframe.contentWindow, + 0, 0, 0, 0, 0, + false, false, false, false, + 0, null); + link.dispatchEvent(event); + }); + await waitForLinkNavigation; + + // Load another dummy page. Verify that both the outgoing parent and + // iframe pages are stored in the bfcache. + await promisePageNavigation({ + uri: getHttpUrl("bug301397_4.html"), + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "iframe parent", + persisted: true }, + { type: "pagehide", + title: "iframe content #2", + persisted: true }, + { type: "pageshow", + title: "dummy page, no iframe", + persisted: false } ], // false on initial load + }); + + // Go back. The iframe should show the second page loaded in it. + // Both the parent and the iframe pages should be loaded from + // the bfcache. + await promisePageNavigation({ + back: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "dummy page, no iframe", + persisted: true }, + { type: "pageshow", + persisted: true, + title: "iframe content #2" }, + { type: "pageshow", + persisted: true, + title: "iframe parent" } ], + }); + + verifyIframeInnerHtml("You made it"); + + // Go gack again. The iframe should show the first page loaded in it. + await promisePageNavigation({ + back: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "iframe content #2", + persisted: false }, // false, subframe nav + { type: "pageshow", + title: "iframe content #1", + // false since this page was never stored + // in the bfcache in the first place + persisted: false } ], + }); + + verifyIframeInnerHtml("go to next page"); + + // Go back to the generic page. Now go forward to the last page, + // again verifying that the iframe shows the first and second + // pages in order. + await promisePageNavigation({ + back: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "iframe parent", + persisted: true }, + { type: "pagehide", + title: "iframe content #1", + persisted: true }, + { type: "pageshow", + title: "generic page", + persisted: true } ], + }); + + await promisePageNavigation({ + forward: true, + eventsToListenFor: ["pageshow"], + expectedEvents: [ {type: "pageshow", + title: "iframe content #1", + persisted: true}, + {type: "pageshow", + title: "iframe parent", + persisted: true} ], + }); + + verifyIframeInnerHtml("go to next page"); + + await promisePageNavigation({ + forward: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "iframe content #1", + persisted: false }, // false, subframe nav + { type: "pageshow", + title: "iframe content #2", + // false because the page wasn't stored in + // bfcache last time it was unloaded + persisted: false } ], + }); + + verifyIframeInnerHtml("You made it"); + + await promisePageNavigation({ + forward: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "iframe parent", + persisted: true }, + { type: "pagehide", + title: "iframe content #2", + persisted: true }, + { type: "pageshow", + title: "dummy page, no iframe", + persisted: true } ], + }); + + // Go back once more, and again verify that the iframe shows the + // second page loaded in it. + await promisePageNavigation({ + back: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "dummy page, no iframe", + persisted: true }, + { type: "pageshow", + persisted: true, + title: "iframe content #2" }, + { type: "pageshow", + persisted: true, + title: "iframe parent" } ], + }); + + verifyIframeInnerHtml("You made it"); + + Services.prefs.clearUserPref("browser.navigation.requireUserInteraction"); + // Tell the framework the test is finished. + finish(); + } + + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" messagemanagergroup="test" remote="true" /> +</window> diff --git a/docshell/test/chrome/bug303267.html b/docshell/test/chrome/bug303267.html new file mode 100644 index 0000000000..21b9f30311 --- /dev/null +++ b/docshell/test/chrome/bug303267.html @@ -0,0 +1,23 @@ +<html> +<head> + <title> + bug303267.html + </title> + </head> +<body onpageshow="showpageshowcount()"> +<script> +var pageshowcount = 0; +function showpageshowcount() { + pageshowcount++; + var div1 = document.getElementById("div1"); + while (div1.firstChild) { + div1.firstChild.remove(); + } + div1.appendChild(document.createTextNode( + "pageshowcount: " + pageshowcount)); +} +</script> +<div id="div1"> + </div> +</body> +</html> diff --git a/docshell/test/chrome/bug303267_window.xhtml b/docshell/test/chrome/bug303267_window.xhtml new file mode 100644 index 0000000000..f985b4da59 --- /dev/null +++ b/docshell/test/chrome/bug303267_window.xhtml @@ -0,0 +1,83 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="303267Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="setTimeout(runTests, 0);" + title="bug 303267 test"> + + <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + //// + // Bug 303267: When a page is displayed from the bfcache, the script globals should + // remain intact from the page's initial load. + // + async function runTests() + { + // Load an initial test page which should be saved in the bfcache. + var navData = { + uri: getHttpUrl("bug303267.html"), + eventsToListenFor: ["pageshow"], + expectedEvents: [ {type: "pageshow", title: "bug303267.html"} ], + }; + await promisePageNavigation(navData); + + // Save the HTML of the test page for later comparison. + var originalHTML = await getInnerHTMLById("div1"); + + // Load a second test page. The first test page's pagehide event should + // have the .persisted property set to true, indicating that it was + // stored in the bfcache. + navData = { + uri: "data:text/html,<html><head><title>page2</title></head>" + + "<body>bug303267, page2</body></html>", + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ {type: "pagehide", + title: "bug303267.html", + persisted: true}, + {type: "pageshow", + title: "page2"} ], + }; + await promisePageNavigation(navData); + + // Go back. Verify that the pageshow event for the original test page + // had a .persisted property of true, indicating that it came from the + // bfcache. + navData = { + back: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ {type: "pagehide", + title: "page2"}, + {type: "pageshow", + title: "bug303267.html", + persisted: true} ], + }; + await promisePageNavigation(navData); + + // After going back, if showpagecount() could access a global variable + // and change the test div's innerHTML, then we pass. Otherwise, it + // threw an exception and the following test will fail. + var newHTML = await getInnerHTMLById("div1"); + isnot(originalHTML, + newHTML, "HTML not updated on pageshow; javascript broken?"); + + // Tell the framework the test is finished. + finish(); + } + + //// + // Return the innerHTML of a particular element in the content document. + // + function getInnerHTMLById(id) { + return SpecialPowers.spawn(TestWindow.getBrowser(), [id], (id) => { + return content.document.getElementById(id).innerHTML; + }); + } + + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" remote="true" /> +</window> diff --git a/docshell/test/chrome/bug311007_window.xhtml b/docshell/test/chrome/bug311007_window.xhtml new file mode 100644 index 0000000000..13ae5e16e3 --- /dev/null +++ b/docshell/test/chrome/bug311007_window.xhtml @@ -0,0 +1,204 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="311007Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="startup();" + title="bug 311007 test"> + + <script src="chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js"></script> + <script type="application/javascript"><![CDATA[ + // `content` is the id of the browser element used for the test. + /* global content */ +/* + Regression test for bug 283733 and bug 307027. + + Bug 283733 + "accessing a relative anchor in a secure page removes the + locked icon and yellow background UI" + + Bug 307027 + "Going back from secure page to error page does not clear yellow bar" + + And enhancements: + + Bug 478927 + onLocationChange should notify whether or not loading an error page. + + */ + +const kDNSErrorURI = "https://example/err.html"; +const kSecureURI = + "https://example.com/tests/docshell/test/navigation/blank.html"; + +/* + Step 1: load a network error page. <err.html> Not Secure + Step 2: load a secure page. <blank.html> Secure + Step 3: a secure page + hashchange. <blank.html#foo> Secure (bug 283733) + Step 4: go back to the error page. <err.html> Not Secure (bug 307027) + */ + +var gListener = null; + +function WebProgressListener() { + this._callback = null; +} + +WebProgressListener.prototype = { + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), + + onLocationChange(aWebProgress, aRequest, aLocation, aFlags) { + setTimeout(this._callback, 0, aWebProgress, aRequest, aLocation, aFlags); + }, + + set callback(aVal) { + this._callback = aVal; + } +}; + +function startup() { + gListener = new WebProgressListener(); + + document.getElementById("content") + .webProgress + .addProgressListener(gListener, + Ci.nsIWebProgress + .NOTIFY_LOCATION); + + setTimeout(step1A, 0); +} + +/****************************************************************************** + * Step 1: Load an error page, and confirm UA knows it's insecure. + ******************************************************************************/ + +function step1A() { + gListener.callback = step1B; + content.location = kDNSErrorURI; +} + +function step1B(aWebProgress, aRequest, aLocation, aFlags) { + is(aLocation.spec, kDNSErrorURI, "Error page's URI (1)"); + + ok(!(aFlags & Ci.nsIWebProgressListener + .LOCATION_CHANGE_SAME_DOCUMENT), + "DocShell loaded a document (1)"); + + ok((aFlags & Ci.nsIWebProgressListener + .LOCATION_CHANGE_ERROR_PAGE), + "This page is an error page."); + + ok(!(document.getElementById("content") + .browsingContext + .secureBrowserUI.state & + Ci.nsIWebProgressListener.STATE_IS_SECURE), + "This is not a secure page (1)"); + + /* Go to step 2. */ + setTimeout(step2A, 0); +} + +/****************************************************************************** + * Step 2: Load a HTTPS page, and confirm it's secure. + ******************************************************************************/ + +function step2A() { + gListener.callback = step2B; + content.location = kSecureURI; +} + +function step2B(aWebProgress, aRequest, aLocation, aFlags) { + is(aLocation.spec, kSecureURI, "A URI on HTTPS (2)"); + + ok(!(aFlags & Ci.nsIWebProgressListener + .LOCATION_CHANGE_SAME_DOCUMENT), + "DocShell loaded a document (2)"); + + ok(!(aFlags & Ci.nsIWebProgressListener + .LOCATION_CHANGE_ERROR_PAGE), + "This page is not an error page."); + + ok((document.getElementById("content") + .browsingContext + .secureBrowserUI.state & + Ci.nsIWebProgressListener.STATE_IS_SECURE), + "This is a secure page (2)"); + + /* Go to step 3. */ + setTimeout(step3A, 0); +} + +/***************************************************************************** + * Step 3: Trigger hashchange within a secure page, and confirm UA knows + * it's secure. (Bug 283733) + *****************************************************************************/ + +function step3A() { + gListener.callback = step3B; + content.location += "#foo"; +} + +function step3B(aWebProgress, aRequest, aLocation, aFlags) { + is(aLocation.spec, kSecureURI + "#foo", "hashchange on HTTPS (3)"); + + ok((aFlags & Ci.nsIWebProgressListener + .LOCATION_CHANGE_SAME_DOCUMENT), + "We are in the same document as before (3)"); + + ok(!(aFlags & Ci.nsIWebProgressListener + .LOCATION_CHANGE_ERROR_PAGE), + "This page is not an error page."); + + ok((document.getElementById("content") + .browsingContext + .secureBrowserUI.state & + Ci.nsIWebProgressListener.STATE_IS_SECURE), + "This is a secure page (3)"); + + /* Go to step 4. */ + setTimeout(step4A, 0); +} + +/***************************************************************************** + * Step 4: Go back from a secure page to an error page, and confirm UA knows + * it's not secure. (Bug 307027) + *****************************************************************************/ + +function step4A() { + gListener.callback = step4B; + content.history.go(-2); +} + +function step4B(aWebProgress, aRequest, aLocation, aFlags) { + is(aLocation.spec, kDNSErrorURI, "Go back to the error URI (4)"); + + ok(!(aFlags & Ci.nsIWebProgressListener + .LOCATION_CHANGE_SAME_DOCUMENT), + "DocShell loaded a document (4)"); + + ok((aFlags & Ci.nsIWebProgressListener + .LOCATION_CHANGE_ERROR_PAGE), + "This page is an error page."); + + ok(!(document.getElementById("content") + .browsingContext + .secureBrowserUI.state & + Ci.nsIWebProgressListener.STATE_IS_SECURE), + "This is not a secure page (4)"); + + /* End. */ + document.getElementById("content") + .webProgress.removeProgressListener(gListener); + gListener = null; + finish(); +} + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" src="about:blank"/> +</window> diff --git a/docshell/test/chrome/bug321671_window.xhtml b/docshell/test/chrome/bug321671_window.xhtml new file mode 100644 index 0000000000..e8fb9fbc06 --- /dev/null +++ b/docshell/test/chrome/bug321671_window.xhtml @@ -0,0 +1,128 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="321671Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="setTimeout(runTest, 0);" + title="bug 321671 test"> + + <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false); + + // Maximum number of entries in the bfcache for this session history. + // This number is hardcoded in docshell code. In the test, we'll + // navigate through enough pages so that we hit one that's been + // evicted from the bfcache because it's farther from the current + // page than this number. + const MAX_BFCACHE_PAGES = 3; + + //// + // Bug 321671: Scroll position should be retained when moving backwards and + // forwards through pages when bfcache is enabled. + // + async function runTest() + { + // Variable to hold the scroll positions of the test pages. + var scrollPositions = []; + + // Make sure bfcache is on. + enableBFCache(true); + + // Load enough test pages that so the first one is evicted from the + // bfcache, scroll down on each page, and save the + // current scroll position before continuing. Verify that each + // page we're navigating away from is initially put into the bfcache. + for (var i = 0; i <= MAX_BFCACHE_PAGES + 1; i++) { + let eventsToListenFor = ["pageshow"]; + let expectedEvents = [ { type: "pageshow", + title: "bug321671 page" + (i + 1) } ]; + if (i > 0) { + eventsToListenFor.push("pagehide"); + expectedEvents.unshift({ type: "pagehide", + persisted: true, + title: "bug321671 page" + i }); + } + await promisePageNavigation( { + uri: "data:text/html,<html><head><title>bug321671 page" + (i + 1) + + "</title></head>" + + "<body><table border='1' width='300' height='1000'>" + + "<tbody><tr><td>" + + " page " + (i + 1) + ": foobar foobar foobar foobar " + + "</td></tr></tbody></table> " + + "</body></html>", + eventsToListenFor, + expectedEvents, + } ); + + let { initialScrollY, scrollY } = await SpecialPowers.spawn(TestWindow.getBrowser(), [i], (i) => { + let initialScrollY = content.scrollY; + content.scrollByLines(10 + (2 * i)); + return { initialScrollY, scrollY: content.scrollY }; + }); + is(initialScrollY, 0, + "Page initially has non-zero scrollY position"); + ok(scrollY > 0, + "Page has zero scrollY position after scrolling"); + scrollPositions[i] = scrollY; + } + + // Go back to the first page, one page at a time. For each 'back' + // action, verify that its vertical scroll position is restored + // correctly. Verify that the last page in the sequence + // does not come from the bfcache. Again verify that all pages + // that we navigate away from are initially + // stored in the bfcache. + for (i = MAX_BFCACHE_PAGES + 1; i > 0; i--) { + await promisePageNavigation( { + back: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "bug321671 page" + (i+1), + persisted: true }, + { type: "pageshow", + title: "bug321671 page" + i, + persisted: i > 1 } ], + } ); + + is(await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + return content.scrollY; + }), scrollPositions[i-1], + "Scroll position not restored while going back!"); + } + + // Traverse history forward now, and verify scroll position is still + // restored. Similar to the backward traversal, verify that all + // but the last page in the sequence comes from the bfcache. Also + // verify that all of the pages get stored in the bfcache when we + // navigate away from them. + for (i = 1; i <= MAX_BFCACHE_PAGES + 1; i++) { + await promisePageNavigation( { + forward: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + persisted: true, + title: "bug321671 page" + i }, + { type: "pageshow", + persisted: i < MAX_BFCACHE_PAGES + 1, + title: "bug321671 page" + (i + 1) } ], + } ); + + is(await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + return content.scrollY; + }), scrollPositions[i], + "Scroll position not restored while going forward!"); + } + + Services.prefs.clearUserPref("browser.navigation.requireUserInteraction"); + // Tell the framework the test is finished. + finish(); + } + + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" remote="true" /> +</window> diff --git a/docshell/test/chrome/bug360511_case1.html b/docshell/test/chrome/bug360511_case1.html new file mode 100644 index 0000000000..cca043bb66 --- /dev/null +++ b/docshell/test/chrome/bug360511_case1.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html style="height: 100%"> +<head> + <title> + bug360511 case 1 + </title> + </head> +<body style="height: 100%"> +<a id="link1" href="#bottom">jump to bottom</a> +<div id="div1" style="height: 200%; border: thin solid black;"> + hello large div + </div> + <a name="bottom">here's the bottom of the page</a> +</body> +</html> diff --git a/docshell/test/chrome/bug360511_case2.html b/docshell/test/chrome/bug360511_case2.html new file mode 100644 index 0000000000..217f47724e --- /dev/null +++ b/docshell/test/chrome/bug360511_case2.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html style="height: 100%"> +<head> + <title> + bug360511 case 2 + </title> + </head> +<body style="height: 100%"> +<a id="link1" href="#bottom">jump to bottom</a> +<div id="div1" style="height: 200%; border: thin solid black;"> + hello large div + </div> + <a name="bottom">here's the bottom of the page</a> +</body> +</html> diff --git a/docshell/test/chrome/bug360511_window.xhtml b/docshell/test/chrome/bug360511_window.xhtml new file mode 100644 index 0000000000..3a22b081a1 --- /dev/null +++ b/docshell/test/chrome/bug360511_window.xhtml @@ -0,0 +1,127 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="360511Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="setTimeout(runTest, 0);" + title="bug 360511 test"> + + <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false); + + function getScrollY() + { + return SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + return content.scrollY; + }); + } + function getLocation() + { + return SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + return content.location.href; + }); + } + + //// + // Bug 360511: Fragment uri's in session history should be restored correctly + // upon back navigation. + // + async function runTest() + { + // Case 1: load a page containing a fragment link; the page should be + // stored in the bfcache. + // Case 2: load a page containing a fragment link; the page should NOT + // be stored in the bfcache. + for (var i = 1; i < 3; i++) + { + var url = "bug360511_case" + i + ".html"; + await promisePageNavigation( { + uri: getHttpUrl(url), + preventBFCache: i != 1 + } ); + + // Store the original url for later comparison. + var originalUrl = TestWindow.getBrowser().currentURI.spec; + var originalDocLocation = await getLocation(); + + // Verify we're at the top of the page. + is(await getScrollY(), 0, "Page initially has a non-zero scrollY property"); + + // Click the on the fragment link in the browser, and use setTimeout + // to give the event a chance to be processed. + await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + var event = content.document.createEvent('MouseEvent'); + event.initMouseEvent("click", true, true, content, 0, + 0, 0, 0, 0, + false, false, false, false, 0, null); + content.document.getElementById("link1").dispatchEvent(event); + }); + await promiseNextPaint(); + + // Verify we're no longer at the top of the page. + await promiseTrue(async function() { + return await getScrollY() > 0; + }, 20); + + // Store the fragment url for later comparison. + var fragmentUrl = TestWindow.getBrowser().currentURI.spec; + let fragDocLocation = await getLocation(); + + // Now navigate to any other page + var expectedPageTitle = "bug360511 case " + i; + await promisePageNavigation( { + uri: getHttpUrl("generic.html"), + eventsToListenFor: ["pagehide", "pageshow"], + expectedEvents: [ {type: "pagehide", title: expectedPageTitle, + persisted: i == 1}, + {type: "pageshow"} ], + } ); + + // Go back + await promisePageNavigation( { + back: true, + eventsToListenFor: ["pageshow"], + expectedEvents: [ {type: "pageshow", title: expectedPageTitle, + persisted: i == 1} ], + } ); + + // Verify the current url is the fragment url + is(TestWindow.getBrowser().currentURI.spec, fragmentUrl, + "current url is not the previous fragment url"); + is(await getLocation(), fragDocLocation, + "document.location is not the previous fragment url"); + + // Go back again. Since we're just going from a fragment url to + // parent url, no pageshow event is fired, so don't wait for any + // events. Rather, just wait for the page's scrollY property to + // change. + var originalScrollY = await getScrollY(); + doPageNavigation( { + back: true, + eventsToListenFor: [] + } ); + await promiseTrue( + async function() { + return (await getScrollY() != originalScrollY); + }, 20); + + // Verify the current url is the original url without fragment + is(TestWindow.getBrowser().currentURI.spec, originalUrl, + "current url is not the original url"); + is(await getLocation(), originalDocLocation, + "document.location is not the original url"); + } + + Services.prefs.clearUserPref("browser.navigation.requireUserInteraction"); + // Tell the framework the test is finished. + finish(); + } + + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" remote="true" /> +</window> diff --git a/docshell/test/chrome/bug364461_window.xhtml b/docshell/test/chrome/bug364461_window.xhtml new file mode 100644 index 0000000000..d64a7b9e23 --- /dev/null +++ b/docshell/test/chrome/bug364461_window.xhtml @@ -0,0 +1,253 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="364461Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="runTest();" + title="364461 test"> + + <script src="chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false); + + var gBrowser; + + async function runTest() { + gBrowser = document.getElementById("content"); + + // Tests 1 + 2: + // Back/forward between two simple documents. Bfcache will be used. + + var test1Doc = "data:text/html,<html><head><title>test1</title></head>" + + "<body>test1</body></html>"; + + await promisePageNavigation({ + uri: test1Doc, + eventsToListenFor: ["load", "pageshow"], + expectedEvents: [{type: "load", title: "test1"}, + {type: "pageshow", title: "test1", persisted: false}], + }); + + var test2Doc = "data:text/html,<html><head><title>test2</title></head>" + + "<body>test2</body></html>"; + + await promisePageNavigation({ + uri: test2Doc, + eventsToListenFor: ["load", "pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", title: "test1", persisted: true}, + {type: "load", title: "test2"}, + {type: "pageshow", title: "test2", persisted: false}], + }); + + await promisePageNavigation({ + back: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", title: "test2", persisted: true}, + {type: "pageshow", title: "test1", persisted: true}], + }); + + await promisePageNavigation({ + forward: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", title: "test1", persisted: true}, + {type: "pageshow", title: "test2", persisted: true}], + }); + + // Tests 3 + 4: + // Back/forward between a two-level deep iframed document and a simple + // document. Bfcache will be used and events should be dispatched to + // all frames. + + var test3Doc = "data:text/html,<html><head><title>test3</title>" + + "</head><body>" + + "<iframe src='data:text/html," + + "<html><head><title>test3-nested1</title></head>" + + "<body>test3-nested1" + + "<iframe src=\"data:text/html," + + "<html><head><title>test3-nested2</title></head>" + + "<body>test3-nested2</body></html>\">" + + "</iframe>" + + "</body></html>'>" + + "</iframe>" + + "</body></html>"; + + await promisePageNavigation({ + uri: test3Doc, + eventsToListenFor: ["load", "pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", title: "test2", persisted: true}, + {type: "load", title: "test3-nested2"}, + {type: "pageshow", title: "test3-nested2", persisted: false}, + {type: "load", title: "test3-nested1"}, + {type: "pageshow", title: "test3-nested1", persisted: false}, + {type: "load", title: "test3"}, + {type: "pageshow", title: "test3", persisted: false}], + }); + + var test4Doc = "data:text/html,<html><head><title>test4</title></head>" + + "<body>test4</body></html>"; + + await promisePageNavigation({ + uri: test4Doc, + eventsToListenFor: ["load", "pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", title: "test3", persisted: true}, + {type: "pagehide", title: "test3-nested1", persisted: true}, + {type: "pagehide", title: "test3-nested2", persisted: true}, + {type: "load", title: "test4"}, + {type: "pageshow", title: "test4", persisted: false}], + }); + + await promisePageNavigation({ + back: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", title: "test4", persisted: true}, + {type: "pageshow", title: "test3-nested2", persisted: true}, + {type: "pageshow", title: "test3-nested1", persisted: true}, + {type: "pageshow", title: "test3", persisted: true}], + }); + + // This is where the two nested pagehide are not dispatched in bug 364461 + await promisePageNavigation({ + forward: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", title: "test3", persisted: true}, + {type: "pagehide", title: "test3-nested1", persisted: true}, + {type: "pagehide", title: "test3-nested2", persisted: true}, + {type: "pageshow", title: "test4", persisted: true}], + }); + + // Tests 5 + 6: + // Back/forward between a document containing an unload handler and a + // a simple document. Bfcache won't be used for the first one (see + // http://developer.mozilla.org/en/docs/Using_Firefox_1.5_caching). + + var test5Doc = "data:text/html,<html><head><title>test5</title></head>" + + "<body onunload='while(false) { /* nop */ }'>" + + "test5</body></html>"; + + await promisePageNavigation({ + uri: test5Doc, + eventsToListenFor: ["load", "pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", title: "test4", persisted: true}, + {type: "load", title: "test5"}, + {type: "pageshow", title: "test5", persisted: false}], + }); + + var test6Doc = "data:text/html,<html><head><title>test6</title></head>" + + "<body>test6</body></html>"; + + await promisePageNavigation({ + uri: test6Doc, + eventsToListenFor: ["load", "unload", "pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", title: "test5", persisted: false}, + {type: "unload", title: "test5"}, + {type: "load", title: "test6"}, + {type: "pageshow", title: "test6", persisted: false}], + }); + + await promisePageNavigation({ + back: true, + eventsToListenFor: ["load", "pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", title: "test6", persisted: true}, + {type: "load", title: "test5"}, + {type: "pageshow", title: "test5", persisted: false}], + }); + + await promisePageNavigation({ + forward: true, + eventsToListenFor: ["unload", "pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", title: "test5", persisted: false}, + {type: "unload", title: "test5"}, + {type: "pageshow", title: "test6", persisted: true}], + }); + + // Test 7: + // Testcase from https://bugzilla.mozilla.org/show_bug.cgi?id=384977#c10 + // Check that navigation is not blocked after a document is restored + // from bfcache + + var test7Doc = "data:text/html,<html><head><title>test7</title>" + + "</head><body>" + + "<iframe src='data:text/html," + + "<html><head><title>test7-nested1</title></head>" + + "<body>test7-nested1<br/>" + + "<a href=\"data:text/plain,aaa\" target=\"_top\">" + + "Click me, hit back, click me again</a>" + + "</body></html>'>" + + "</iframe>" + + "</body></html>"; + + await promisePageNavigation({ + uri: test7Doc, + eventsToListenFor: ["load", "pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", title: "test6", persisted: true}, + {type: "load", title: "test7-nested1"}, + {type: "pageshow", title: "test7-nested1", persisted: false}, + {type: "load", title: "test7"}, + {type: "pageshow", title: "test7", persisted: false}], + }); + + // Simulates a click on the link inside the iframe + function clickIframeLink() { + SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + var iframe = content.document.getElementsByTagName("iframe")[0]; + var w = iframe.contentWindow; + var d = iframe.contentDocument; + + var evt = d.createEvent("MouseEvents"); + evt.initMouseEvent("click", true, true, w, + 0, 0, 0, 0, 0, false, false, false, false, 0, null); + d.getElementsByTagName("a")[0].dispatchEvent(evt); + }); + } + + let clicked = promisePageNavigation({ + eventsToListenFor: ["load", "pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", title: "test7", persisted: true}, + {type: "pagehide", title: "test7-nested1", persisted: true}, + {type: "load"}, + {type: "pageshow", persisted: false}], + waitForEventsOnly: true, + }); + clickIframeLink(); + await clicked; + + is(gBrowser.currentURI.spec, "data:text/plain,aaa", + "Navigation is blocked when clicking link"); + + await promisePageNavigation({ + back: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", persisted: true}, + {type: "pageshow", title: "test7-nested1", persisted: true}, + {type: "pageshow", title: "test7", persisted: true}], + }); + + clicked = promisePageNavigation({ + eventsToListenFor: ["load", "pageshow", "pagehide"], + expectedEvents: [{type: "pagehide", title: "test7", persisted: true}, + {type: "pagehide", title: "test7-nested1", persisted: true}, + {type: "load"}, + {type: "pageshow", persisted: false}], + waitForEventsOnly: true, + }); + clickIframeLink(); + await clicked; + + is(gBrowser.currentURI.spec, "data:text/plain,aaa", + "Navigation is blocked when clicking link"); + + Services.prefs.clearUserPref("browser.navigation.requireUserInteraction"); + finish(); + } + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" remote="true" /> +</window> diff --git a/docshell/test/chrome/bug396519_window.xhtml b/docshell/test/chrome/bug396519_window.xhtml new file mode 100644 index 0000000000..5cbc6f5a41 --- /dev/null +++ b/docshell/test/chrome/bug396519_window.xhtml @@ -0,0 +1,132 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="396519Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="runTest();" + title="396519 test"> + + <script src="chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false); + + var gTestCount = 0; + + async function navigateAndTest(params, expected) { + await promisePageNavigation(params); + ++gTestCount; + await doTest(expected); + } + + async function doTest(expected) { + function check(testCount, expected) { + let history; + if (SpecialPowers.Services.appinfo.sessionHistoryInParent) { + history = this.browsingContext.sessionHistory; + } else { + history = this.content.browsingContext.childSessionHistory.legacySHistory; + } + if (history.count == expected.length) { + for (let i = 0; i < history.count; i++) { + var shEntry = history.getEntryAtIndex(i). + QueryInterface(Ci.nsISHEntry); + is(shEntry.isInBFCache, expected[i], `BFCache for shentry[${i}], test ${testCount}`); + } + + // Make sure none of the SHEntries share bfcache entries with one + // another. + for (let i = 0; i < history.count; i++) { + for (let j = 0; j < history.count; j++) { + if (j == i) + continue; + + let shentry1 = history.getEntryAtIndex(i) + .QueryInterface(Ci.nsISHEntry); + let shentry2 = history.getEntryAtIndex(j) + .QueryInterface(Ci.nsISHEntry); + ok(!shentry1.sharesDocumentWith(shentry2), + 'Test ' + testCount + ': shentry[' + i + "] shouldn't " + + "share document with shentry[" + j + ']'); + } + } + } + else { + is(history.count, expected.length, "Wrong history length in test "+testCount); + } + } + + if (SpecialPowers.Services.appinfo.sessionHistoryInParent) { + check.call(TestWindow.getBrowser(), gTestCount, expected); + } else { + await SpecialPowers.spawn(TestWindow.getBrowser(), [gTestCount, expected], check); + } + } + + async function runTest() { + // Tests 1 + 2: + // Back/forward between two simple documents. Bfcache will be used. + + var test1Doc = "data:text/html,<html><head><title>test1</title></head>" + + "<body>test1</body></html>"; + + await navigateAndTest({ + uri: test1Doc, + }, [false]); + + var test2Doc = test1Doc.replace(/1/,"2"); + + await navigateAndTest({ + uri: test2Doc, + }, [true, false]); + + await navigateAndTest({ + uri: test1Doc, + }, [true, true, false]); + + await navigateAndTest({ + uri: test2Doc, + }, [true, true, true, false]); + + await navigateAndTest({ + uri: test1Doc, + }, [false, true, true, true, false]); + + await navigateAndTest({ + uri: test2Doc, + }, [false, false, true, true, true, false]); + + await navigateAndTest({ + back: true, + }, [false, false, true, true, false, true]); + + await navigateAndTest({ + forward: true, + }, [false, false, true, true, true, false]); + + await navigateAndTest({ + gotoIndex: 1, + }, [false, false, true, true, true, false]); + + await navigateAndTest({ + back: true, + }, [false, true, true, true, false, false]); + + await navigateAndTest({ + gotoIndex: 5, + }, [false, false, true, true, false, false]); + + Services.prefs.clearUserPref("browser.navigation.requireUserInteraction"); + finish(); + } + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" remote="true" /> +</window> diff --git a/docshell/test/chrome/bug396649_window.xhtml b/docshell/test/chrome/bug396649_window.xhtml new file mode 100644 index 0000000000..16a243bc74 --- /dev/null +++ b/docshell/test/chrome/bug396649_window.xhtml @@ -0,0 +1,119 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="396649Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="setTimeout(nextTest, 0);" + title="bug 396649 test"> + + <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false); + + // Define the generator-iterator for the tests. + var tests = testIterator(); + + // Maximum number of entries in the bfcache for this session history. + // This number is hardcoded in docshell code. In the test, we'll + // navigate through enough pages so that we hit one that's been + // evicted from the bfcache because it's farther from the current + // page than this number. + const MAX_BFCACHE_PAGES = 3; + + //// + // Execute the next test in the generator function. + // + function nextTest() { + tests.next(); + } + + //// + // Generator function for test steps for bug 396649: Content + // viewers should be evicted from bfcache when going back if more + // than MAX_BFCACHE_PAGES from the current index. + // + function* testIterator() + { + // Make sure bfcache is on. + enableBFCache(true); + + // Load enough pages so that the first loaded is eviced from + // the bfcache, since it is greater the MAX_BFCACHE_PAGES from + // the current position in the session history. Verify all + // of the pages are initially stored in the bfcache when + // they're unloaded. + for (var i = 0; i <= MAX_BFCACHE_PAGES + 1; i++) { + let eventsToListenFor = ["pageshow"]; + let expectedEvents = [ { type: "pageshow", + title: "bug396649 page" + i } ]; + if (i > 0) { + eventsToListenFor.push("pagehide"); + expectedEvents.unshift({ type: "pagehide", + title: "bug396649 page" + (i-1) }); + } + doPageNavigation( { + uri: "data:text/html,<!DOCTYPE html><html>" + + "<head><title>bug396649 page" + i + + "</title></head>" + + "<body>" + + "test page " + i + + "</body></html>", + eventsToListenFor, + expectedEvents, + onNavComplete: nextTest + } ); + yield undefined; + } + + // Go back to the first page, one page at a time. The first + // MAX_BFCACHE_PAGES pages loaded via back should come from the bfcache, + // the last should not, since it should have been evicted during the + // previous navigation sequence. Verify all pages are initially stored + // in the bfcache when they're unloaded. + for (i = MAX_BFCACHE_PAGES + 1; i > 0; i--) { + doPageNavigation( { + back: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "bug396649 page" + i, + persisted: true }, + { type: "pageshow", + title: "bug396649 page" + (i - 1), + persisted: i > 1 } ], + onNavComplete: nextTest + } ); + yield undefined; + } + + // Traverse history forward now. Again, the first MAX_BFCACHE_PAGES + // pages should come from the bfcache, the last should not, + // since it should have been evicted during the backwards + // traversal above. Verify all pages are initially stored + // in the bfcache when they're unloaded. + for (i = 1; i <= MAX_BFCACHE_PAGES + 1; i++) { + doPageNavigation( { + forward: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "bug396649 page" + (i-1), + persisted: true }, + { type: "pageshow", + title: "bug396649 page" + i, + persisted: i < MAX_BFCACHE_PAGES + 1 } ], + onNavComplete: nextTest + } ); + yield undefined; + } + + Services.prefs.clearUserPref("browser.navigation.requireUserInteraction"); + // Tell the framework the test is finished. + finish(); + } + + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" remote="true" /> +</window> diff --git a/docshell/test/chrome/bug449778_window.xhtml b/docshell/test/chrome/bug449778_window.xhtml new file mode 100644 index 0000000000..1bbb8d4b2b --- /dev/null +++ b/docshell/test/chrome/bug449778_window.xhtml @@ -0,0 +1,107 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<window title="Mozilla Bug 449778" onload="doTheTest()" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <hbox id="parent"> + </hbox> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + /* globals SimpleTest, is */ + var imports = [ "SimpleTest", "is" ]; + for (var name of imports) { + window[name] = window.arguments[0][name]; + } + + function $(id) { + return document.getElementById(id); + } + + function addBrowser(parent, id, width, height) { + var b = + document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "browser"); + b.setAttribute("type", "content"); + b.setAttribute("id", id); + b.setAttribute("width", width); + b.setAttribute("height", height); + $(parent).appendChild(b); + } + addBrowser("parent", "f1", 300, 200); + addBrowser("parent", "f2", 300, 200); + + /** Test for Bug 449778 **/ + var doc1 = "data:text/html,<html><body>This is a test</body></html>"; + var doc2 = "data:text/html,<html><body>This is a second test</body></html>"; + var doc3 = "data:text/html,<html><body>This is a <script>var evt = document.createEvent('Events'); evt.initEvent('testEvt', true, true); document.dispatchEvent(evt);</script>third test</body></html>"; + + + $("f1").setAttribute("src", doc1); + $("f2").setAttribute("src", doc2); + + function doTheTest() { + var strs = { "f1": "", "f2" : "" }; + function attachListener(node, type) { + var listener = function(e) { + if (strs[node.id]) strs[node.id] += " "; + strs[node.id] += node.id + ".page" + type; + } + node.addEventListener("page" + type, listener); + + listener.detach = function() { + node.removeEventListener("page" + type, listener); + } + return listener; + } + + var l1 = attachListener($("f1"), "show"); + var l2 = attachListener($("f1"), "hide"); + var l3 = attachListener($("f2"), "show"); + var l4 = attachListener($("f2"), "hide"); + + $("f1").swapDocShells($("f2")); + + is(strs.f1, "f1.pagehide f1.pageshow", + "Expected hide then show on first loaded page"); + is(strs.f2, "f2.pagehide f2.pageshow", + "Expected hide then show on second loaded page"); + + function listener2() { + $("f2").removeEventListener("testEvt", listener2); + + strs = { "f1": "", "f2" : "" }; + + $("f1").swapDocShells($("f2")); + is(strs.f1, "f1.pagehide", + "Expected hide on already-loaded page, then nothing"); + is(strs.f2, "f2.pageshow f2.pagehide f2.pageshow", + "Expected show on still-loading page, then hide on it, then show " + + "on already-loaded page"); + + strs = { "f1": "", "f2" : "" }; + + $("f1").addEventListener("pageshow", listener3); + } + + function listener3() { + $("f1").removeEventListener("pageshow", listener3); + + is(strs.f1, "f1.pageshow", + "Expected show as our page finishes loading"); + is(strs.f2, "", "Expected no more events here."); + + l1.detach(); + l2.detach(); + l3.detach(); + l4.detach(); + + window.close(); + SimpleTest.finish(); + } + + $("f2").addEventListener("testEvt", listener2, false, true); + $("f2").setAttribute("src", doc3); + } + + ]]></script> +</window> diff --git a/docshell/test/chrome/bug449780_window.xhtml b/docshell/test/chrome/bug449780_window.xhtml new file mode 100644 index 0000000000..3b0096d2e9 --- /dev/null +++ b/docshell/test/chrome/bug449780_window.xhtml @@ -0,0 +1,80 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<window title="Mozilla Bug 449780" onload="setTimeout(doTheTest, 0);" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <hbox id="parent"> + </hbox> + + <!-- test code goes here --> + <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + function addBrowser(parent, width, height) { + var b = + document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "browser"); + b.setAttribute("type", "content"); + b.setAttribute("id", "content"); + b.setAttribute("width", width); + b.setAttribute("height", height); + b.setAttribute("remote", SpecialPowers.Services.appinfo.sessionHistoryInParent); + document.getElementById("parent").appendChild(b); + return b; + } + + let f1 = addBrowser("parent", 300, 200); + + /** Test for Bug 449780 **/ + var doc1 = "data:text/html,<html><body>This is a test</body></html>"; + var doc2 = "data:text/html,<html><body>This is a second test</body></html>"; + + async function doTheTest() { + await promisePageNavigation({ + uri: doc1, + }); + let { origDOM, modifiedDOM } = await SpecialPowers.spawn(f1, [], () => { + var origDOM = content.document.documentElement.innerHTML; + content.document.body.textContent = "Modified"; + var modifiedDOM = content.document.documentElement.innerHTML; + isnot(origDOM, modifiedDOM, "DOM should be different"); + return { origDOM, modifiedDOM }; + }); + + await promisePageNavigation({ + uri: doc2, + }); + + await promisePageNavigation({ + back: true, + }); + + await SpecialPowers.spawn(f1, [modifiedDOM], (modifiedDOM) => { + is(content.document.documentElement.innerHTML, modifiedDOM, "Should have been bfcached"); + }); + + await promisePageNavigation({ + forward: true, + }); + + f1.removeAttribute("id"); + let f2 = addBrowser("parent", 300, 200); + + // Make sure there's a document or the swap will fail. + await promisePageNavigation({ + uri: "about:blank", + }); + + f1.swapDocShells(f2); + + await promisePageNavigation({ + back: true, + }); + + await SpecialPowers.spawn(f2, [origDOM], (origDOM) => { + is(content.document.documentElement.innerHTML, origDOM, "Should not have been bfcached"); + }); + + finish(); + } + ]]></script> +</window> diff --git a/docshell/test/chrome/bug454235-subframe.xhtml b/docshell/test/chrome/bug454235-subframe.xhtml new file mode 100644 index 0000000000..a8b6178e65 --- /dev/null +++ b/docshell/test/chrome/bug454235-subframe.xhtml @@ -0,0 +1,7 @@ +<window title="Mozilla Bug 454235 SubFrame" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <deck flex="1"> + <browser id="topBrowser" src="about:mozilla"/> + <browser id="burriedBrowser" src="about:mozilla"/> + </deck> +</window> diff --git a/docshell/test/chrome/bug582176_window.xhtml b/docshell/test/chrome/bug582176_window.xhtml new file mode 100644 index 0000000000..f468d17b8b --- /dev/null +++ b/docshell/test/chrome/bug582176_window.xhtml @@ -0,0 +1,74 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="303267Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="runTest();" + title="bug 582176 test"> + + <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + //// + // Bug 582176. + // + async function runTest() + { + enableBFCache(true); + + var notificationCount = 0; + + let onGlobalCreation = () => { + ++notificationCount; + }; + + await promisePageNavigation({ + uri: "http://mochi.test:8888/chrome/docshell/test/chrome/582176_dummy.html", + onGlobalCreation, + }); + is(await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + let testVar = content.testVar; + content.testVar = 1; + return testVar; + }), undefined, + "variable unexpectedly there already"); + is(notificationCount, 1, "Should notify on first navigation"); + + await promisePageNavigation({ + uri: "http://mochi.test:8888/chrome/docshell/test/chrome/582176_dummy.html?2", + onGlobalCreation, + }); + is(await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + return content.testVar; + }), undefined, + "variable should no longer be there"); + is(notificationCount, 2, "Should notify on second navigation"); + + await promisePageNavigation({ + back: true, + }); + is(await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + return content.testVar; + }), 1, + "variable should still be there"); + is(notificationCount, 2, "Should not notify on back navigation"); + + await promisePageNavigation({ + uri: "http://mochi.test:8888/chrome/docshell/test/chrome/582176_xml.xml", + onGlobalCreation, + }); + is(await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + return content.document.body.textContent; + }), "xslt result", + "Transform performed successfully"); + is(notificationCount, 3, "Should notify only once on XSLT navigation"); + + // Tell the framework the test is finished. + finish(); + } + + ]]></script> + <browser type="content" primary="true" flex="1" id="content" remote="true" /> +</window> diff --git a/docshell/test/chrome/bug608669.xhtml b/docshell/test/chrome/bug608669.xhtml new file mode 100644 index 0000000000..993f24051c --- /dev/null +++ b/docshell/test/chrome/bug608669.xhtml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<window title="Mozilla Bug 608669 - Blank page" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="notifyOpener();"> + <description flex="1" value="This window is intentionally left blank"/> + <script type="application/javascript"> + function notifyOpener() { + if (opener) { + opener.postMessage("load", "*"); + } + } + </script> +</window> diff --git a/docshell/test/chrome/bug662200_window.xhtml b/docshell/test/chrome/bug662200_window.xhtml new file mode 100644 index 0000000000..7c6f656f26 --- /dev/null +++ b/docshell/test/chrome/bug662200_window.xhtml @@ -0,0 +1,119 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="303267Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="setTimeout(runTest, 0);" + title="bug 662200 test"> + + <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false); + + //// + // Bug 662200 + // + async function runTest() + { + // Load the first test page + var navData = { + uri: getHttpUrl("662200a.html"), + eventsToListenFor: ["pageshow"], + expectedEvents: [ {type: "pageshow", title: "A"} ], + }; + await promisePageNavigation(navData); + + // Load the second test page. + navData = { + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ {type: "pagehide", + title: "A"}, + {type: "pageshow", + title: "B"} ], + } + let clicked = promisePageEvents(navData); + SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + var link = content.document.getElementById("link"); + var event = content.document.createEvent("MouseEvents"); + event.initMouseEvent("click", true, true, content, + 0, 0, 0, 0, 0, false, false, false, false, 0, null); + link.dispatchEvent(event); + }); + await clicked; + + // Load the third test page. + navData = { + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ {type: "pagehide", + title: "B"}, + {type: "pageshow", + title: "C"} ], + }; + clicked = promisePageEvents(navData); + SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + var link = content.document.getElementById("link"); + var event = content.document.createEvent("MouseEvents"); + event.initMouseEvent("click", true, true, content, + 0, 0, 0, 0, 0, false, false, false, false, 0, null); + link.dispatchEvent(event); + }); + await clicked; + + // Go back. + navData = { + back: true, + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ {type: "pagehide", + title: "C"}, + {type: "pageshow", + title: "B"} ], + }; + await promisePageNavigation(navData); + + // Reload. + navData = { + eventsToListenFor: ["pageshow", "pagehide"], + expectedEvents: [ {type: "pagehide", + title: "B"}, + {type: "pageshow", + title: "B"} ], + }; + // Asking the docshell harness to reload for us will call reload on + // nsDocShell which has different behavior than the reload on nsSHistory + // so we call reloadCurrentEntry() (which is equivalent to reload(0) and + // visible from JS) explicitly here. + let reloaded = promisePageEvents(navData); + if (SpecialPowers.Services.appinfo.sessionHistoryInParent) { + TestWindow.getBrowser().browsingContext.sessionHistory.reloadCurrentEntry(); + } else { + SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory.legacySHistory.reloadCurrentEntry(); + }); + } + await reloaded; + + // After this sequence of events, we should be able to go back and forward + is(TestWindow.getBrowser().canGoBack, true, "Should be able to go back!"); + is(TestWindow.getBrowser().canGoForward, true, "Should be able to go forward!"); + let requestedIndex; + if (SpecialPowers.Services.appinfo.sessionHistoryInParent) { + requestedIndex = TestWindow.getBrowser().browsingContext.sessionHistory.requestedIndex; + } else { + requestedIndex = await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + return docShell.sessionHistory.legacySHistory.requestedIndex; + }) + } + is(requestedIndex, -1, "Requested index should be cleared!"); + + Services.prefs.clearUserPref("browser.navigation.requireUserInteraction"); + // Tell the framework the test is finished. + finish(); + } + + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" remote="true" /> +</window> diff --git a/docshell/test/chrome/bug690056_window.xhtml b/docshell/test/chrome/bug690056_window.xhtml new file mode 100644 index 0000000000..a48e14ce8f --- /dev/null +++ b/docshell/test/chrome/bug690056_window.xhtml @@ -0,0 +1,171 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="690056Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="setTimeout(nextTest, 0);" + title="bug 6500056 test"> + + <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + var tests = testIterator(); + + function nextTest() { + tests.next(); + } + + // Makes sure that we fire the visibilitychange events + function* testIterator() { + // Enable bfcache + enableBFCache(8); + + // Load something for a start + doPageNavigation({ + uri: 'data:text/html,<title>initial load</title>', + onNavComplete: nextTest + }); + yield undefined; + + // Now load a new page + doPageNavigation({ + uri: 'data:text/html,<title>new load</title>', + eventsToListenFor: [ "pageshow", "pagehide", "visibilitychange" ], + expectedEvents: [ { type: "pagehide", + title: "initial load", + persisted: true }, + { type: "visibilitychange", + title: "initial load", + visibilityState: "hidden", + hidden: true }, + // No visibilitychange events fired for initial pageload + { type: "pageshow", + title: "new load", + persisted: false }, // false on initial load + ], + onNavComplete: nextTest + }); + yield undefined; + + // Now go back + doPageNavigation({ + back: true, + eventsToListenFor: [ "pageshow", "pagehide", "visibilitychange" ], + expectedEvents: [ { type: "pagehide", + title: "new load", + persisted: true }, + { type: "visibilitychange", + title: "new load", + visibilityState: "hidden", + hidden: true }, + { type: "visibilitychange", + title: "initial load", + visibilityState: "visible", + hidden: false }, + { type: "pageshow", + title: "initial load", + persisted: true }, + ], + onNavComplete: nextTest + }); + yield undefined; + + // And forward + doPageNavigation({ + forward: true, + eventsToListenFor: [ "pageshow", "pagehide", "visibilitychange" ], + expectedEvents: [ { type: "pagehide", + title: "initial load", + persisted: true }, + { type: "visibilitychange", + title: "initial load", + visibilityState: "hidden", + hidden: true }, + { type: "visibilitychange", + title: "new load", + visibilityState: "visible", + hidden: false }, + { type: "pageshow", + title: "new load", + persisted: true }, + ], + onNavComplete: nextTest + }); + yield undefined; + + waitForPageEvents({ + eventsToListenFor: [ "visibilitychange" ], + expectedEvents: [ { type: "visibilitychange", + title: "new load", + visibilityState: "hidden", + hidden: true }, + ], + onNavComplete: nextTest + }); + + // Now flip our docshell to not active + TestWindow.getBrowser().docShellIsActive = false; + yield undefined; + + // And navigate back; there should be no visibility state transitions + doPageNavigation({ + back: true, + eventsToListenFor: [ "pageshow", "pagehide", "visibilitychange" ], + expectedEvents: [ { type: "pagehide", + title: "new load", + persisted: true }, + { type: "pageshow", + title: "initial load", + persisted: true }, + ], + unexpectedEvents: [ "visibilitychange" ], + onNavComplete: nextTest + }); + yield undefined; + + waitForPageEvents({ + eventsToListenFor: [ "visibilitychange" ], + expectedEvents: [ { type: "visibilitychange", + title: "initial load", + visibilityState: "visible", + hidden: false }, + ], + onNavComplete: nextTest + }); + + // Now set the docshell active again + TestWindow.getBrowser().docShellIsActive = true; + yield undefined; + + // And forward + doPageNavigation({ + forward: true, + eventsToListenFor: [ "pageshow", "pagehide", "visibilitychange" ], + expectedEvents: [ { type: "pagehide", + title: "initial load", + persisted: true }, + { type: "visibilitychange", + title: "initial load", + visibilityState: "hidden", + hidden: true }, + { type: "visibilitychange", + title: "new load", + visibilityState: "visible", + hidden: false }, + { type: "pageshow", + title: "new load", + persisted: true }, + ], + onNavComplete: nextTest + }); + yield undefined; + + // Tell the framework the test is finished. + finish(); + } + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" remote="true" /> +</window> diff --git a/docshell/test/chrome/bug846906.html b/docshell/test/chrome/bug846906.html new file mode 100644 index 0000000000..a289417ea8 --- /dev/null +++ b/docshell/test/chrome/bug846906.html @@ -0,0 +1,10 @@ +<html> + <head> + <title> + </title> + </head> + <body> + <div id="div1" style="width:1024px; height:768px; border:none;"> + </div> + </body> +</html> diff --git a/docshell/test/chrome/bug89419.sjs b/docshell/test/chrome/bug89419.sjs new file mode 100644 index 0000000000..7172690a9a --- /dev/null +++ b/docshell/test/chrome/bug89419.sjs @@ -0,0 +1,12 @@ +function handleRequest(request, response) { + var redirectstate = "/docshell/test/chrome/bug89419.sjs"; + response.setStatusLine("1.1", 302, "Found"); + if (getState(redirectstate) == "") { + response.setHeader("Location", "red.png", false); + setState(redirectstate, "red"); + } else { + response.setHeader("Location", "blue.png", false); + setState(redirectstate, ""); + } + response.setHeader("Cache-Control", "no-cache", false); +} diff --git a/docshell/test/chrome/bug89419_window.xhtml b/docshell/test/chrome/bug89419_window.xhtml new file mode 100644 index 0000000000..12b9dec650 --- /dev/null +++ b/docshell/test/chrome/bug89419_window.xhtml @@ -0,0 +1,69 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="89419Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="setTimeout(runTests, 0);" + title="bug 89419 test"> + + <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script> + + <script type="application/javascript"><![CDATA[ + //// + // A visited link should have the :visited style applied + // to it when displayed on a page which was fetched from + // the bfcache. + // + async function runTests() { + // Disable rcwn to make cache behavior deterministic. + var {SpecialPowers} = window.arguments[0]; + await SpecialPowers.pushPrefEnv({"set":[["network.http.rcwn.enabled", false]]}); + + // Load a test page containing an image referring to the sjs that returns + // a different redirect every time it's loaded. + await new Promise(resolve => { + doPageNavigation({ + uri: getHttpUrl("89419.html"), + onNavComplete: resolve, + preventBFCache: true, + }); + }) + + var first = await snapshotWindow(TestWindow.getWindow()); + + await new Promise(resolve => { + doPageNavigation({ + uri: "about:blank", + onNavComplete: resolve, + }); + }); + + var second = await snapshotWindow(TestWindow.getWindow()); + function snapshotsEqual(snap1, snap2) { + return compareSnapshots(snap1, snap2, true)[0]; + } + ok(!snapshotsEqual(first, second), "about:blank should not be the same as the image web page"); + + await new Promise(resolve => { + doPageNavigation({ + back: true, + onNavComplete: resolve, + }); + }); + + var third = await snapshotWindow(TestWindow.getWindow()); + ok(!snapshotsEqual(third, second), "going back should not be the same as about:blank"); + ok(snapshotsEqual(first, third), "going back should be the same as the initial load"); + + // Tell the framework the test is finished. + finish(); + } + + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" src="about:blank"/> +</window> diff --git a/docshell/test/chrome/bug909218.html b/docshell/test/chrome/bug909218.html new file mode 100644 index 0000000000..a11fa6000d --- /dev/null +++ b/docshell/test/chrome/bug909218.html @@ -0,0 +1,11 @@ +<html>
+<head>
+ <link rel="stylesheet" type="text/css" href="http://mochi.test:8888/tests/SimpleTest/test.css">
+ <script src="bug909218.js"></script>
+</head>
+<body>
+ <img src="http://mochi.test:8888/tests/docshell/test/chrome/red.png">
+ <!-- an iframe so we can check these too get the correct flags -->
+ <iframe src="generic.html"/>
+</body>
+</html>
diff --git a/docshell/test/chrome/bug909218.js b/docshell/test/chrome/bug909218.js new file mode 100644 index 0000000000..2222480cd3 --- /dev/null +++ b/docshell/test/chrome/bug909218.js @@ -0,0 +1,2 @@ +// This file exists just to ensure that we load it with the correct flags. +dump("bug909218.js loaded\n"); diff --git a/docshell/test/chrome/bug92598_window.xhtml b/docshell/test/chrome/bug92598_window.xhtml new file mode 100644 index 0000000000..5a10ff250a --- /dev/null +++ b/docshell/test/chrome/bug92598_window.xhtml @@ -0,0 +1,89 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="92598Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="onLoad();" + title="92598 test"> + + <script src="chrome://mochikit/content/chrome-harness.js" /> + <script type="application/javascript" src="docshell_helpers.js" /> + <script type="application/javascript"><![CDATA[ + var gBrowser; + var gTestsIterator; + + function onLoad() { + gBrowser = document.getElementById("content"); + gTestsIterator = testsIterator(); + nextTest(); + } + + function nextTest() { + gTestsIterator.next(); + } + + function* testsIterator() { + // Load a page with a no-cache header, followed by a simple page + // On pagehide, first page should report it is not being persisted + var test1DocURI = "http://mochi.test:8888/chrome/docshell/test/chrome/92598_nostore.html"; + + doPageNavigation({ + uri: test1DocURI, + eventsToListenFor: ["load", "pageshow"], + expectedEvents: [ { type: "load", + title: "test1" }, + { type: "pageshow", + title: "test1", + persisted: false } ], + onNavComplete: nextTest + }); + yield undefined; + + var test2Doc = "data:text/html,<html><head><title>test2</title></head>" + + "<body>test2</body></html>"; + + doPageNavigation({ + uri: test2Doc, + eventsToListenFor: ["load", "pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "test1", + persisted: false }, + { type: "load", + title: "test2" }, + { type: "pageshow", + title: "test2", + persisted: false } ], + onNavComplete: nextTest + }); + yield undefined; + + // Now go back in history. First page should not have been cached. + // Check persisted property to confirm + doPageNavigation({ + back: true, + eventsToListenFor: ["load", "pageshow", "pagehide"], + expectedEvents: [ { type: "pagehide", + title: "test2", + persisted: true }, + { type: "load", + title: "test1" }, + { type: "pageshow", + title: "test1", + persisted: false } ], + onNavComplete: nextTest + }); + yield undefined; + + finish(); + } + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" remote="true" /> +</window> diff --git a/docshell/test/chrome/chrome.ini b/docshell/test/chrome/chrome.ini new file mode 100644 index 0000000000..2d126b84b7 --- /dev/null +++ b/docshell/test/chrome/chrome.ini @@ -0,0 +1,105 @@ +[DEFAULT] +skip-if = os == 'android' +support-files = + 662200a.html + 662200b.html + 662200c.html + 89419.html + bug113934_window.xhtml + bug215405_window.xhtml + bug293235.html + bug293235_p2.html + bug293235_window.xhtml + bug294258_testcase.html + bug294258_window.xhtml + bug298622_window.xhtml + bug301397_1.html + bug301397_2.html + bug301397_3.html + bug301397_4.html + bug301397_window.xhtml + bug303267.html + bug303267_window.xhtml + bug311007_window.xhtml + bug321671_window.xhtml + bug360511_case1.html + bug360511_case2.html + bug360511_window.xhtml + bug364461_window.xhtml + bug396519_window.xhtml + bug396649_window.xhtml + bug449778_window.xhtml + bug449780_window.xhtml + bug454235-subframe.xhtml + bug608669.xhtml + bug662200_window.xhtml + bug690056_window.xhtml + bug846906.html + bug89419_window.xhtml + bug909218.html + bug909218.js + docshell_helpers.js + DocShellHelpers.sys.mjs + file_viewsource_forbidden_in_iframe.html + generic.html + mozFrameType_window.xhtml + test_docRedirect.sjs + +[test_allowContentRetargeting.html] +[test_bug112564.xhtml] +support-files = + bug112564_window.xhtml + 112564_nocache.html + 112564_nocache.html^headers^ +[test_bug113934.xhtml] +[test_bug215405.xhtml] +[test_bug293235.xhtml] +skip-if = true # bug 1393441 +[test_bug294258.xhtml] +[test_bug298622.xhtml] +[test_bug301397.xhtml] +skip-if = (os == 'win' && processor == 'aarch64') # bug 1533819 +[test_bug303267.xhtml] +[test_bug311007.xhtml] +[test_bug321671.xhtml] +[test_bug360511.xhtml] +skip-if = (os == 'win' && processor == 'x86') +[test_bug364461.xhtml] +skip-if = (os == 'win' && processor == 'aarch64') # bug 1533814 +[test_bug396519.xhtml] +[test_bug396649.xhtml] +[test_bug428288.html] +[test_bug449778.xhtml] +[test_bug449780.xhtml] +[test_bug453650.xhtml] +[test_bug454235.xhtml] +[test_bug456980.xhtml] +[test_bug565388.xhtml] +skip-if = true # Bug 1026815,Bug 1546159 +[test_bug582176.xhtml] +support-files = + 582176_dummy.html + 582176_xml.xml + 582176_xslt.xsl + bug582176_window.xhtml +[test_bug608669.xhtml] +[test_bug662200.xhtml] +[test_bug690056.xhtml] +[test_bug789773.xhtml] +[test_bug846906.xhtml] +[test_bug89419.xhtml] +[test_bug909218.html] +[test_bug92598.xhtml] +support-files = + 92598_nostore.html + 92598_nostore.html^headers^ + bug92598_window.xhtml +[test_open_and_immediately_close_opener.html] +# This bug only manifests in the non-e10s window open codepath. The test +# should be updated to make sure it still opens a new window in the parent +# process if and when we start running chrome mochitests with e10s enabled. +skip-if = e10s +[test_mozFrameType.xhtml] +[test_viewsource_forbidden_in_iframe.xhtml] +skip-if = true # bug 1019315 +[test_docRedirect.xhtml] diff --git a/docshell/test/chrome/docshell_helpers.js b/docshell/test/chrome/docshell_helpers.js new file mode 100644 index 0000000000..facd627b45 --- /dev/null +++ b/docshell/test/chrome/docshell_helpers.js @@ -0,0 +1,758 @@ +if (!window.opener && window.arguments) { + window.opener = window.arguments[0]; +} +/** + * Import common SimpleTest methods so that they're usable in this window. + */ +/* globals SimpleTest, is, isnot, ok, onerror, todo, todo_is, todo_isnot */ +var imports = [ + "SimpleTest", + "is", + "isnot", + "ok", + "onerror", + "todo", + "todo_is", + "todo_isnot", +]; +for (var name of imports) { + window[name] = window.opener.wrappedJSObject[name]; +} +const { BrowserTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/BrowserTestUtils.sys.mjs" +); + +const ACTOR_MODULE_URI = + "chrome://mochitests/content/chrome/docshell/test/chrome/DocShellHelpers.sys.mjs"; +const { DocShellHelpersParent } = ChromeUtils.importESModule(ACTOR_MODULE_URI); +// Some functions assume chrome-harness.js has been loaded. +/* import-globals-from ../../../testing/mochitest/chrome-harness.js */ + +/** + * Define global constants and variables. + */ +const NAV_NONE = 0; +const NAV_BACK = 1; +const NAV_FORWARD = 2; +const NAV_GOTOINDEX = 3; +const NAV_URI = 4; +const NAV_RELOAD = 5; + +var gExpectedEvents; // an array of events which are expected to +// be triggered by this navigation +var gUnexpectedEvents; // an array of event names which are NOT expected +// to be triggered by this navigation +var gFinalEvent; // true if the last expected event has fired +var gUrisNotInBFCache = []; // an array of uri's which shouldn't be stored +// in the bfcache +var gNavType = NAV_NONE; // defines the most recent navigation type +// executed by doPageNavigation +var gOrigMaxTotalViewers = undefined; // original value of max_total_viewers, // to be restored at end of test + +var gExtractedPath = null; // used to cache file path for extracting files from a .jar file + +/** + * The doPageNavigation() function performs page navigations asynchronously, + * listens for specified events, and compares actual events with a list of + * expected events. When all expected events have occurred, an optional + * callback can be notified. The parameter passed to this function is an + * object with the following properties: + * + * uri: if !undefined, the browser will navigate to this uri + * + * back: if true, the browser will execute goBack() + * + * forward: if true, the browser will execute goForward() + * + * gotoIndex: if a number, the browser will execute gotoIndex() with + * the number as index + * + * reload: if true, the browser will execute reload() + * + * eventsToListenFor: an array containing one or more of the following event + * types to listen for: "pageshow", "pagehide", "onload", + * "onunload". If this property is undefined, only a + * single "pageshow" events will be listened for. If this + * property is explicitly empty, [], then no events will + * be listened for. + * + * expectedEvents: an array of one or more expectedEvent objects, + * corresponding to the events which are expected to be + * fired for this navigation. Each object has the + * following properties: + * + * type: one of the event type strings + * title (optional): the title of the window the + * event belongs to + * persisted (optional): the event's expected + * .persisted attribute + * + * This function will verify that events with the + * specified properties are fired in the same order as + * specified in the array. If .title or .persisted + * properties for an expectedEvent are undefined, those + * properties will not be verified for that particular + * event. + * + * This property is ignored if eventsToListenFor is + * undefined or []. + * + * preventBFCache: if true, an RTCPeerConnection will be added to the loaded + * page to prevent it from being bfcached. This property + * has no effect when eventsToListenFor is []. + * + * onNavComplete: a callback which is notified after all expected events + * have occurred, or after a timeout has elapsed. This + * callback is not notified if eventsToListenFor is []. + * onGlobalCreation: a callback which is notified when a DOMWindow is created + * (implemented by observing + * "content-document-global-created") + * + * There must be an expectedEvent object for each event of the types in + * eventsToListenFor which is triggered by this navigation. For example, if + * eventsToListenFor = [ "pagehide", "pageshow" ], then expectedEvents + * must contain an object for each pagehide and pageshow event which occurs as + * a result of this navigation. + */ +// eslint-disable-next-line complexity +function doPageNavigation(params) { + // Parse the parameters. + let back = params.back ? params.back : false; + let forward = params.forward ? params.forward : false; + let gotoIndex = params.gotoIndex ? params.gotoIndex : false; + let reload = params.reload ? params.reload : false; + let uri = params.uri ? params.uri : false; + let eventsToListenFor = + typeof params.eventsToListenFor != "undefined" + ? params.eventsToListenFor + : ["pageshow"]; + gExpectedEvents = + typeof params.eventsToListenFor == "undefined" || !eventsToListenFor.length + ? undefined + : params.expectedEvents; + gUnexpectedEvents = + typeof params.eventsToListenFor == "undefined" || !eventsToListenFor.length + ? undefined + : params.unexpectedEvents; + let preventBFCache = + typeof [params.preventBFCache] == "undefined" + ? false + : params.preventBFCache; + let waitOnly = + typeof params.waitForEventsOnly == "boolean" && params.waitForEventsOnly; + + // Do some sanity checking on arguments. + let navigation = ["back", "forward", "gotoIndex", "reload", "uri"].filter(k => + params.hasOwnProperty(k) + ); + if (navigation.length > 1) { + throw new Error(`Can't specify both ${navigation[0]} and ${navigation[1]}`); + } else if (!navigation.length && !waitOnly) { + throw new Error( + "Must specify back or forward or gotoIndex or reload or uri" + ); + } + if (params.onNavComplete && !eventsToListenFor.length) { + throw new Error("Can't use onNavComplete when eventsToListenFor == []"); + } + if (params.preventBFCache && !eventsToListenFor.length) { + throw new Error("Can't use preventBFCache when eventsToListenFor == []"); + } + if (params.preventBFCache && waitOnly) { + throw new Error("Can't prevent bfcaching when only waiting for events"); + } + if (waitOnly && typeof params.onNavComplete == "undefined") { + throw new Error( + "Must specify onNavComplete when specifying waitForEventsOnly" + ); + } + if (waitOnly && navigation.length) { + throw new Error( + "Can't specify a navigation type when using waitForEventsOnly" + ); + } + for (let anEventType of eventsToListenFor) { + let eventFound = false; + if (anEventType == "pageshow" && !gExpectedEvents) { + eventFound = true; + } + if (gExpectedEvents) { + for (let anExpectedEvent of gExpectedEvents) { + if (anExpectedEvent.type == anEventType) { + eventFound = true; + } + } + } + if (gUnexpectedEvents) { + for (let anExpectedEventType of gUnexpectedEvents) { + if (anExpectedEventType == anEventType) { + eventFound = true; + } + } + } + if (!eventFound) { + throw new Error( + `Event type ${anEventType} is specified in ` + + "eventsToListenFor, but not in expectedEvents" + ); + } + } + + // If the test explicitly sets .eventsToListenFor to [], don't wait for any + // events. + gFinalEvent = !eventsToListenFor.length; + + // Add observers as needed. + let observers = new Map(); + if (params.hasOwnProperty("onGlobalCreation")) { + observers.set("content-document-global-created", params.onGlobalCreation); + } + + // Add an event listener for each type of event in the .eventsToListenFor + // property of the input parameters, and add an observer for all the topics + // in the observers map. + let cleanup; + let useActor = TestWindow.getBrowser().isRemoteBrowser; + if (useActor) { + ChromeUtils.registerWindowActor("DocShellHelpers", { + parent: { + esModuleURI: ACTOR_MODULE_URI, + }, + child: { + esModuleURI: ACTOR_MODULE_URI, + events: { + pageshow: { createActor: true, capture: true }, + pagehide: { createActor: true, capture: true }, + load: { createActor: true, capture: true }, + unload: { createActor: true, capture: true }, + visibilitychange: { createActor: true, capture: true }, + }, + observers: observers.keys(), + }, + allFrames: true, + }); + DocShellHelpersParent.eventsToListenFor = eventsToListenFor; + DocShellHelpersParent.observers = observers; + + cleanup = () => { + DocShellHelpersParent.eventsToListenFor = null; + DocShellHelpersParent.observers = null; + ChromeUtils.unregisterWindowActor("DocShellHelpers"); + }; + } else { + for (let eventType of eventsToListenFor) { + dump("TEST: registering a listener for " + eventType + " events\n"); + TestWindow.getBrowser().addEventListener( + eventType, + pageEventListener, + true + ); + } + if (observers.size > 0) { + let observer = (_, topic) => { + observers.get(topic).call(); + }; + for (let topic of observers.keys()) { + Services.obs.addObserver(observer, topic); + } + + // We only need to do cleanup for the observer, the event listeners will + // go away with the window. + cleanup = () => { + for (let topic of observers.keys()) { + Services.obs.removeObserver(observer, topic); + } + }; + } + } + + if (cleanup) { + // Register a cleanup function on domwindowclosed, to avoid contaminating + // other tests if we bail out early because of an error. + Services.ww.registerNotification(function windowClosed( + subject, + topic, + data + ) { + if (topic == "domwindowclosed" && subject == window) { + Services.ww.unregisterNotification(windowClosed); + cleanup(); + } + }); + } + + // Perform the specified navigation. + if (back) { + gNavType = NAV_BACK; + TestWindow.getBrowser().goBack(); + } else if (forward) { + gNavType = NAV_FORWARD; + TestWindow.getBrowser().goForward(); + } else if (typeof gotoIndex == "number") { + gNavType = NAV_GOTOINDEX; + TestWindow.getBrowser().gotoIndex(gotoIndex); + } else if (uri) { + gNavType = NAV_URI; + BrowserTestUtils.loadURI(TestWindow.getBrowser(), uri); + } else if (reload) { + gNavType = NAV_RELOAD; + TestWindow.getBrowser().reload(); + } else if (waitOnly) { + gNavType = NAV_NONE; + } else { + throw new Error("No valid navigation type passed to doPageNavigation!"); + } + + // If we're listening for events and there is an .onNavComplete callback, + // wait for all events to occur, and then call doPageNavigation_complete(). + if (eventsToListenFor.length && params.onNavComplete) { + waitForTrue( + function() { + return gFinalEvent; + }, + function() { + doPageNavigation_complete( + eventsToListenFor, + params.onNavComplete, + preventBFCache, + useActor, + cleanup + ); + } + ); + } else if (cleanup) { + cleanup(); + } +} + +/** + * Finish doPageNavigation(), by removing event listeners, adding an unload + * handler if appropriate, and calling the onNavComplete callback. This + * function is called after all the expected events for this navigation have + * occurred. + */ +function doPageNavigation_complete( + eventsToListenFor, + onNavComplete, + preventBFCache, + useActor, + cleanup +) { + if (useActor) { + if (preventBFCache) { + let actor = TestWindow.getBrowser().browsingContext.currentWindowGlobal.getActor( + "DocShellHelpers" + ); + actor.sendAsyncMessage("docshell_helpers:preventBFCache"); + } + } else { + // Unregister our event listeners. + dump("TEST: removing event listeners\n"); + for (let eventType of eventsToListenFor) { + TestWindow.getBrowser().removeEventListener( + eventType, + pageEventListener, + true + ); + } + + // If the .preventBFCache property was set, add an RTCPeerConnection to + // prevent the page from being bfcached. + if (preventBFCache) { + let win = TestWindow.getWindow(); + win.blockBFCache = new win.RTCPeerConnection(); + } + } + + if (cleanup) { + cleanup(); + } + + let uri = TestWindow.getBrowser().currentURI.spec; + if (preventBFCache) { + // Save the current uri in an array of uri's which shouldn't be + // stored in the bfcache, for later verification. + if (!(uri in gUrisNotInBFCache)) { + gUrisNotInBFCache.push(uri); + } + } else if (gNavType == NAV_URI) { + // If we're navigating to a uri and .preventBFCache was not + // specified, splice it out of gUrisNotInBFCache if it's there. + gUrisNotInBFCache.forEach(function(element, index, array) { + if (element == uri) { + array.splice(index, 1); + } + }, this); + } + + // Notify the callback now that we're done. + onNavComplete.call(); +} + +function promisePageNavigation(params) { + if (params.hasOwnProperty("onNavComplete")) { + throw new Error( + "Can't use a onNavComplete completion callback with promisePageNavigation." + ); + } + return new Promise(resolve => { + params.onNavComplete = resolve; + doPageNavigation(params); + }); +} + +/** + * Allows a test to wait for page navigation events, and notify a + * callback when they've all been received. This works exactly the + * same as doPageNavigation(), except that no navigation is initiated. + */ +function waitForPageEvents(params) { + params.waitForEventsOnly = true; + doPageNavigation(params); +} + +function promisePageEvents(params) { + if (params.hasOwnProperty("onNavComplete")) { + throw new Error( + "Can't use a onNavComplete completion callback with promisePageEvents." + ); + } + return new Promise(resolve => { + params.waitForEventsOnly = true; + params.onNavComplete = resolve; + doPageNavigation(params); + }); +} + +/** + * The event listener which listens for expectedEvents. + */ +function pageEventListener( + event, + originalTargetIsHTMLDocument = HTMLDocument.isInstance(event.originalTarget) +) { + try { + dump( + "TEST: eventListener received a " + + event.type + + " event for page " + + event.originalTarget.title + + ", persisted=" + + event.persisted + + "\n" + ); + } catch (e) { + // Ignore any exception. + } + + // If this page shouldn't be in the bfcache because it was previously + // loaded with .preventBFCache, make sure that its pageshow event + // has .persisted = false, even if the test doesn't explicitly test + // for .persisted. + if ( + event.type == "pageshow" && + (gNavType == NAV_BACK || + gNavType == NAV_FORWARD || + gNavType == NAV_GOTOINDEX) + ) { + let uri = TestWindow.getBrowser().currentURI.spec; + if (uri in gUrisNotInBFCache) { + ok( + !event.persisted, + "pageshow event has .persisted = false, even " + + "though it was loaded with .preventBFCache previously\n" + ); + } + } + + if (typeof gUnexpectedEvents != "undefined") { + is( + gUnexpectedEvents.indexOf(event.type), + -1, + "Should not get unexpected event " + event.type + ); + } + + // If no expected events were specified, mark the final event as having been + // triggered when a pageshow event is fired; this will allow + // doPageNavigation() to return. + if (typeof gExpectedEvents == "undefined" && event.type == "pageshow") { + waitForNextPaint(function() { + gFinalEvent = true; + }); + return; + } + + // If there are explicitly no expected events, but we receive one, it's an + // error. + if (!gExpectedEvents.length) { + ok(false, "Unexpected event (" + event.type + ") occurred"); + return; + } + + // Grab the next expected event, and compare its attributes against the + // actual event. + let expected = gExpectedEvents.shift(); + + is( + event.type, + expected.type, + "A " + + expected.type + + " event was expected, but a " + + event.type + + " event occurred" + ); + + if (typeof expected.title != "undefined") { + ok( + originalTargetIsHTMLDocument, + "originalTarget for last " + event.type + " event not an HTMLDocument" + ); + is( + event.originalTarget.title, + expected.title, + "A " + + event.type + + " event was expected for page " + + expected.title + + ", but was fired for page " + + event.originalTarget.title + ); + } + + if (typeof expected.persisted != "undefined") { + is( + event.persisted, + expected.persisted, + "The persisted property of the " + + event.type + + " event on page " + + event.originalTarget.location + + " had an unexpected value" + ); + } + + if ("visibilityState" in expected) { + is( + event.originalTarget.visibilityState, + expected.visibilityState, + "The visibilityState property of the document on page " + + event.originalTarget.location + + " had an unexpected value" + ); + } + + if ("hidden" in expected) { + is( + event.originalTarget.hidden, + expected.hidden, + "The hidden property of the document on page " + + event.originalTarget.location + + " had an unexpected value" + ); + } + + // If we're out of expected events, let doPageNavigation() return. + if (!gExpectedEvents.length) { + waitForNextPaint(function() { + gFinalEvent = true; + }); + } +} + +DocShellHelpersParent.eventListener = pageEventListener; + +/** + * End a test. + */ +function finish() { + // Work around bug 467960. + let historyPurged; + if (SpecialPowers.Services.appinfo.sessionHistoryInParent) { + let history = TestWindow.getBrowser().browsingContext?.sessionHistory; + history.purgeHistory(history.count); + historyPurged = Promise.resolve(); + } else { + historyPurged = SpecialPowers.spawn(TestWindow.getBrowser(), [], () => { + let history = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory + .legacySHistory; + history.purgeHistory(history.count); + }); + } + + // If the test changed the value of max_total_viewers via a call to + // enableBFCache(), then restore it now. + if (typeof gOrigMaxTotalViewers != "undefined") { + Services.prefs.setIntPref( + "browser.sessionhistory.max_total_viewers", + gOrigMaxTotalViewers + ); + } + + // Close the test window and signal the framework that the test is done. + let opener = window.opener; + let SimpleTest = opener.wrappedJSObject.SimpleTest; + + // Wait for the window to be closed before finishing the test + Services.ww.registerNotification(function observer(subject, topic, data) { + if (topic == "domwindowclosed") { + Services.ww.unregisterNotification(observer); + SimpleTest.waitForFocus(SimpleTest.finish, opener); + } + }); + + historyPurged.then(_ => { + window.close(); + }); +} + +/** + * Helper function which waits until another function returns true, or until a + * timeout occurs, and then notifies a callback. + * + * Parameters: + * + * fn: a function which is evaluated repeatedly, and when it turns true, + * the onWaitComplete callback is notified. + * + * onWaitComplete: a callback which will be notified when fn() returns + * true, or when a timeout occurs. + * + * timeout: a timeout, in seconds or ms, after which waitForTrue() will + * fail an assertion and then return, even if the fn function never + * returns true. If timeout is undefined, waitForTrue() will never + * time out. + */ +function waitForTrue(fn, onWaitComplete, timeout) { + promiseTrue(fn, timeout).then(() => { + onWaitComplete.call(); + }); +} + +function promiseTrue(fn, timeout) { + if (typeof timeout != "undefined") { + // If timeoutWait is less than 500, assume it represents seconds, and + // convert to ms. + if (timeout < 500) { + timeout *= 1000; + } + } + + // Loop until the test function returns true, or until a timeout occurs, + // if a timeout is defined. + let intervalid, timeoutid; + let condition = new Promise(resolve => { + intervalid = setInterval(async () => { + if (await fn.call()) { + resolve(); + } + }, 20); + }); + if (typeof timeout != "undefined") { + condition = Promise.race([ + condition, + new Promise((_, reject) => { + timeoutid = setTimeout(() => { + reject(); + }, timeout); + }), + ]); + } + return condition + .finally(() => { + clearInterval(intervalid); + }) + .then(() => { + clearTimeout(timeoutid); + }); +} + +function waitForNextPaint(cb) { + requestAnimationFrame(_ => requestAnimationFrame(cb)); +} + +function promiseNextPaint() { + return new Promise(resolve => { + waitForNextPaint(resolve); + }); +} + +/** + * Enable or disable the bfcache. + * + * Parameters: + * + * enable: if true, set max_total_viewers to -1 (the default); if false, set + * to 0 (disabled), if a number, set it to that specific number + */ +function enableBFCache(enable) { + // If this is the first time the test called enableBFCache(), + // store the original value of max_total_viewers, so it can + // be restored at the end of the test. + if (typeof gOrigMaxTotalViewers == "undefined") { + gOrigMaxTotalViewers = Services.prefs.getIntPref( + "browser.sessionhistory.max_total_viewers" + ); + } + + if (typeof enable == "boolean") { + if (enable) { + Services.prefs.setIntPref("browser.sessionhistory.max_total_viewers", -1); + } else { + Services.prefs.setIntPref("browser.sessionhistory.max_total_viewers", 0); + } + } else if (typeof enable == "number") { + Services.prefs.setIntPref( + "browser.sessionhistory.max_total_viewers", + enable + ); + } +} + +/* + * get http root for local tests. Use a single extractJarToTmp instead of + * extracting for each test. + * Returns a file://path if we have a .jar file + */ +function getHttpRoot() { + var location = window.location.href; + location = getRootDirectory(location); + var jar = getJar(location); + if (jar != null) { + if (gExtractedPath == null) { + var resolved = extractJarToTmp(jar); + gExtractedPath = resolved.path; + } + } else { + return null; + } + return "file://" + gExtractedPath + "/"; +} + +/** + * Returns the full HTTP url for a file in the mochitest docshell test + * directory. + */ +function getHttpUrl(filename) { + var root = getHttpRoot(); + if (root == null) { + root = "http://mochi.test:8888/chrome/docshell/test/chrome/"; + } + return root + filename; +} + +/** + * A convenience object with methods that return the current test window, + * browser, and document. + */ +var TestWindow = {}; +TestWindow.getWindow = function() { + return document.getElementById("content").contentWindow; +}; +TestWindow.getBrowser = function() { + return document.getElementById("content"); +}; +TestWindow.getDocument = function() { + return document.getElementById("content").contentDocument; +}; diff --git a/docshell/test/chrome/file_viewsource_forbidden_in_iframe.html b/docshell/test/chrome/file_viewsource_forbidden_in_iframe.html new file mode 100644 index 0000000000..fdecbbdfe1 --- /dev/null +++ b/docshell/test/chrome/file_viewsource_forbidden_in_iframe.html @@ -0,0 +1,11 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Test ifranes for view-source forbidden in iframe tests</title> +</head> +<body> + <iframe id="testIframe"></iframe> + <iframe id="refIframe"></iframe> +</body> +</html> diff --git a/docshell/test/chrome/gen_template.pl b/docshell/test/chrome/gen_template.pl new file mode 100644 index 0000000000..109d6161cd --- /dev/null +++ b/docshell/test/chrome/gen_template.pl @@ -0,0 +1,39 @@ +#!/usr/bin/perl + +# This script makes docshell test case templates. It takes one argument: +# +# -b: a bugnumber +# +# For example, this command: +# +# perl gen_template.pl -b 303267 +# +# Writes test case template files test_bug303267.xhtml and bug303267_window.xhtml +# to the current directory. + +use FindBin; +use Getopt::Long; +GetOptions("b=i"=> \$bug_number); + +$template = "$FindBin::RealBin/test.template.txt"; + +open(IN,$template) or die("Failed to open input file for reading."); +open(OUT, ">>test_bug" . $bug_number . ".xhtml") or die("Failed to open output file for appending."); +while((defined(IN)) && ($line = <IN>)) { + $line =~ s/{BUGNUMBER}/$bug_number/g; + print OUT $line; +} +close(IN); +close(OUT); + +$template = "$FindBin::RealBin/window.template.txt"; + +open(IN,$template) or die("Failed to open input file for reading."); +open(OUT, ">>bug" . $bug_number . "_window.xhtml") or die("Failed to open output file for appending."); +while((defined(IN)) && ($line = <IN>)) { + $line =~ s/{BUGNUMBER}/$bug_number/g; + print OUT $line; +} +close(IN); +close(OUT); + diff --git a/docshell/test/chrome/generic.html b/docshell/test/chrome/generic.html new file mode 100644 index 0000000000..569a78c05a --- /dev/null +++ b/docshell/test/chrome/generic.html @@ -0,0 +1,12 @@ +<html> +<head> + <title> + generic page + </title> + </head> +<body> +<div id="div1" style="height: 1000px; border: thin solid black;"> + A generic page which can be used any time a test needs to load an arbitrary page via http. + </div> +</body> +</html> diff --git a/docshell/test/chrome/mozFrameType_window.xhtml b/docshell/test/chrome/mozFrameType_window.xhtml new file mode 100644 index 0000000000..e5d0126d22 --- /dev/null +++ b/docshell/test/chrome/mozFrameType_window.xhtml @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<window title="Test mozFrameType attribute" + xmlns:html="http://www.w3.org/1999/xhtml" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="runTests();"> + + <html:iframe id="normalFrame"/> + <html:iframe id="typeContentFrame" mozframetype="content"/> + + <script type="application/javascript"><![CDATA[ + function runTests() { + let opener = window.arguments[0]; + let SimpleTest = opener.SimpleTest; + + function getDocShellType(frame) { + return frame.contentWindow.docShell.itemType; + } + + var normalFrame = document.getElementById("normalFrame"); + var typeContentFrame = document.getElementById("typeContentFrame"); + + SimpleTest.is(getDocShellType(normalFrame), Ci.nsIDocShellTreeItem.typeChrome, + "normal iframe in chrome document is typeChrome"); + SimpleTest.is(getDocShellType(typeContentFrame), Ci.nsIDocShellTreeItem.typeContent, + "iframe with mozFrameType='content' in chrome document is typeContent"); + + SimpleTest.executeSoon(function () { + // First focus the parent window and then close this one. + SimpleTest.waitForFocus(function() { + let ww = SpecialPowers.Services.ww; + ww.registerNotification(function windowObs(subject, topic, data) { + if (topic == "domwindowclosed") { + ww.unregisterNotification(windowObs); + + // Don't start the next test synchronously! + SimpleTest.executeSoon(function() { + SimpleTest.finish(); + }); + } + }); + + window.close(); + }, opener); + }); + } + ]]></script> +</window> diff --git a/docshell/test/chrome/red.png b/docshell/test/chrome/red.png Binary files differnew file mode 100644 index 0000000000..aa9ce25263 --- /dev/null +++ b/docshell/test/chrome/red.png diff --git a/docshell/test/chrome/test.template.txt b/docshell/test/chrome/test.template.txt new file mode 100644 index 0000000000..b7dd5e5c23 --- /dev/null +++ b/docshell/test/chrome/test.template.txt @@ -0,0 +1,41 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}.xul +--> +<window title="Mozilla Bug {BUGNUMBER}" + xmlns:html="http://www.w3.org/1999/xhtml" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <title>Test for Bug {BUGNUMBER}</title> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}"> + Mozilla Bug {BUGNUMBER}</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug {BUGNUMBER} **/ + +SimpleTest.waitForExplicitFinish(); +window.open("bug{BUGNUMBER}_window.xul", "bug{BUGNUMBER}", + "chrome,width=600,height=600"); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_allowContentRetargeting.html b/docshell/test/chrome/test_allowContentRetargeting.html new file mode 100644 index 0000000000..b6b830138f --- /dev/null +++ b/docshell/test/chrome/test_allowContentRetargeting.html @@ -0,0 +1,76 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); +addLoadEvent(runNextTest); + +var TEST_URL = "http://mochi.test:8888/tests/docshell/test/chrome/allowContentRetargeting.sjs"; + +function runNextTest() { + var test = tests.shift(); + if (!test) { + SimpleTest.finish(); + return; + } + test(); +} + +var tests = [ + + // Set allowContentRetargeting = false, load a downloadable URL, verify the + // downloadable stops loading. + function basic() { + var iframe = insertIframe(); + iframe.contentWindow.docShell.allowContentRetargeting = false; + loadIframe(iframe); + }, + + // Set allowContentRetargeting = false on parent docshell, load a downloadable + // URL, verify the downloadable stops loading. + function inherit() { + var docshell = window.docShell; + docshell.allowContentRetargeting = false; + loadIframe(insertIframe()); + }, +]; + +function insertIframe() { + var iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + return iframe; +} + +function loadIframe(iframe) { + iframe.setAttribute("src", TEST_URL); + iframe.contentWindow.docShell. + QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIWebProgress). + addProgressListener(progressListener, + Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); +} + +var progressListener = { + onStateChange(webProgress, req, flags, status) { + if (!(flags & Ci.nsIWebProgressListener.STATE_STOP)) + return; + is(Components.isSuccessCode(status), false, + "Downloadable should have failed to load"); + document.querySelector("iframe").remove(); + runNextTest(); + }, + + QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener", "nsISupportsWeakReference"]), +}; + + </script> +</head> +<body> +<p id="display"> +</p> +</body> +</html> diff --git a/docshell/test/chrome/test_bug112564.xhtml b/docshell/test/chrome/test_bug112564.xhtml new file mode 100644 index 0000000000..33fd187eb2 --- /dev/null +++ b/docshell/test/chrome/test_bug112564.xhtml @@ -0,0 +1,37 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=112564 +--> +<window title="Mozilla Bug 112564" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=112564">Mozilla Bug 112564</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 112564 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug112564_window.xhtml", "bug112564", + "chrome,width=600,height=600,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug113934.xhtml b/docshell/test/chrome/test_bug113934.xhtml new file mode 100644 index 0000000000..99b8ae253c --- /dev/null +++ b/docshell/test/chrome/test_bug113934.xhtml @@ -0,0 +1,29 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=113934 +--> +<window title="Mozilla Bug 113934" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=113934" + target="_blank">Mozilla Bug 396519</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + SimpleTest.waitForExplicitFinish(); + + addLoadEvent(function() { + window.openDialog("bug113934_window.xhtml?content", "bug113934", + "chrome,width=800,height=800,noopener", window); + }); + + ]]></script> +</window> diff --git a/docshell/test/chrome/test_bug215405.xhtml b/docshell/test/chrome/test_bug215405.xhtml new file mode 100644 index 0000000000..e40d1d046a --- /dev/null +++ b/docshell/test/chrome/test_bug215405.xhtml @@ -0,0 +1,37 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=215405 +--> +<window title="Mozilla Bug 215405" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=215405">Mozilla Bug 215405</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 215405 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug215405_window.xhtml", "bug215405", + "chrome,width=600,height=600,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug293235.xhtml b/docshell/test/chrome/test_bug293235.xhtml new file mode 100644 index 0000000000..5b0f03f1e9 --- /dev/null +++ b/docshell/test/chrome/test_bug293235.xhtml @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=293235.xul +--> +<window title="Mozilla Bug 293235" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=293235"> + Mozilla Bug 293235</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 293235 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug293235_window.xhtml", "bug293235", + "chrome,width=600,height=600,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug294258.xhtml b/docshell/test/chrome/test_bug294258.xhtml new file mode 100644 index 0000000000..97588b6ab2 --- /dev/null +++ b/docshell/test/chrome/test_bug294258.xhtml @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=294258.xul +--> +<window title="Mozilla Bug 294258" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=294258"> + Mozilla Bug 294258</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 294258 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug294258_window.xhtml", "bug294258", + "chrome,width=600,height=600,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug298622.xhtml b/docshell/test/chrome/test_bug298622.xhtml new file mode 100644 index 0000000000..56eae4df37 --- /dev/null +++ b/docshell/test/chrome/test_bug298622.xhtml @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=298622.xul +--> +<window title="Mozilla Bug 298622" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=298622"> + Mozilla Bug 298622</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 298622 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug298622_window.xhtml", "bug298622", + "chrome,width=600,height=600,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug301397.xhtml b/docshell/test/chrome/test_bug301397.xhtml new file mode 100644 index 0000000000..9bcd2f5212 --- /dev/null +++ b/docshell/test/chrome/test_bug301397.xhtml @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=301397.xul +--> +<window title="Mozilla Bug 301397" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=301397"> + Mozilla Bug 301397</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 301397 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug301397_window.xhtml", "bug301397", + "chrome,width=600,height=600,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug303267.xhtml b/docshell/test/chrome/test_bug303267.xhtml new file mode 100644 index 0000000000..105abff0d9 --- /dev/null +++ b/docshell/test/chrome/test_bug303267.xhtml @@ -0,0 +1,39 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=303267.xul +--> +<window title="Mozilla Bug 303267" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=303267">Mozilla Bug 303267</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +SimpleTest.expectAssertions(0, 1); + +/** Test for Bug 303267 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug303267_window.xhtml", "bug303267", + "chrome,width=600,height=600,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug311007.xhtml b/docshell/test/chrome/test_bug311007.xhtml new file mode 100644 index 0000000000..b4f13e6d9d --- /dev/null +++ b/docshell/test/chrome/test_bug311007.xhtml @@ -0,0 +1,42 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=311007.xul +--> +<window title="Mozilla Bug 311007" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=311007"> + Mozilla Bug 311007</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +if (navigator.platform.startsWith("Win")) { + SimpleTest.expectAssertions(0, 1); +} + +/** Test for Bug 311007 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug311007_window.xhtml", "bug311007", + "chrome,width=600,height=600,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug321671.xhtml b/docshell/test/chrome/test_bug321671.xhtml new file mode 100644 index 0000000000..aa1712bf1f --- /dev/null +++ b/docshell/test/chrome/test_bug321671.xhtml @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=321671.xul +--> +<window title="Mozilla Bug 321671" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=321671"> + Mozilla Bug 321671</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 321671 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug321671_window.xhtml", "bug321671", + "chrome,width=600,height=600,scrollbars,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug360511.xhtml b/docshell/test/chrome/test_bug360511.xhtml new file mode 100644 index 0000000000..cbbadcdd45 --- /dev/null +++ b/docshell/test/chrome/test_bug360511.xhtml @@ -0,0 +1,39 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=360511.xul +--> +<window title="Mozilla Bug 360511" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"> + </script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=360511"> + Mozilla Bug 360511</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 360511 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug360511_window.xhtml", "bug360511", + "chrome,scrollbars,width=600,height=600,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug364461.xhtml b/docshell/test/chrome/test_bug364461.xhtml new file mode 100644 index 0000000000..c753f7825b --- /dev/null +++ b/docshell/test/chrome/test_bug364461.xhtml @@ -0,0 +1,43 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=364461 +--> +<window title="Mozilla Bug 364461" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=364461">Mozilla Bug 364461</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 364461 **/ + +SimpleTest.waitForExplicitFinish(); + +SpecialPowers.pushPrefEnv({ + "set":[["security.data_uri.block_toplevel_data_uri_navigations", false]] +}, runTests); + +function runTests() { + window.openDialog("bug364461_window.xhtml", "bug364461", + "chrome,width=600,height=600,noopener", window); +} +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug396519.xhtml b/docshell/test/chrome/test_bug396519.xhtml new file mode 100644 index 0000000000..a6faea79ae --- /dev/null +++ b/docshell/test/chrome/test_bug396519.xhtml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=396519 +--> +<window title="Mozilla Bug 396519" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=396519" + target="_blank">Mozilla Bug 396519</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + + /** Test for Bug 396519 **/ + + SimpleTest.waitForExplicitFinish(); + window.openDialog("bug396519_window.xhtml", "bug396519", + "chrome,width=600,height=600,noopener", window); + + ]]></script> +</window> diff --git a/docshell/test/chrome/test_bug396649.xhtml b/docshell/test/chrome/test_bug396649.xhtml new file mode 100644 index 0000000000..eda8e924da --- /dev/null +++ b/docshell/test/chrome/test_bug396649.xhtml @@ -0,0 +1,41 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=396649.xul +--> +<window title="Mozilla Bug 396649" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script type="application/javascript" + src= + "chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"> + </script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=396649"> + Mozilla Bug 396649</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 396649 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug396649_window.xhtml", "bug396649", + "chrome,width=600,height=600,scrollbars,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug428288.html b/docshell/test/chrome/test_bug428288.html new file mode 100644 index 0000000000..29b90c677d --- /dev/null +++ b/docshell/test/chrome/test_bug428288.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=428288 +--> +<head> + <title>Test for Bug 428288</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=428288">Mozilla Bug 428288</a> +<p id="display"></p> +<div id="content" style="display: none"> + <iframe name="target"></iframe> + <a id="crashy" target="target" href="about:blank">crash me</a> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +/** Test for Bug 428288 **/ + +function makeClick() { + var event = document.createEvent("MouseEvents"); + event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, + false, false, false, false, 0, null); + document.getElementById("crashy").dispatchEvent(event); + return true; +} + +ok(makeClick(), "Crashes if bug 428288 is present"); + +</script> +</pre> +</body> +</html> + diff --git a/docshell/test/chrome/test_bug449778.xhtml b/docshell/test/chrome/test_bug449778.xhtml new file mode 100644 index 0000000000..67e17164ea --- /dev/null +++ b/docshell/test/chrome/test_bug449778.xhtml @@ -0,0 +1,29 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=449778 +--> +<window title="Mozilla Bug 449778" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=449778" + target="_blank">Mozilla Bug 396519</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + SimpleTest.waitForExplicitFinish(); + + addLoadEvent(function() { + window.openDialog("bug449778_window.xhtml", "bug449778", + "chrome,width=800,height=800,noopener", window); + }); + + ]]></script> +</window> diff --git a/docshell/test/chrome/test_bug449780.xhtml b/docshell/test/chrome/test_bug449780.xhtml new file mode 100644 index 0000000000..43ed3ce25d --- /dev/null +++ b/docshell/test/chrome/test_bug449780.xhtml @@ -0,0 +1,29 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=449780 +--> +<window title="Mozilla Bug 449780" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=449780" + target="_blank">Mozilla Bug 396519</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + SimpleTest.waitForExplicitFinish(); + + addLoadEvent(function() { + window.openDialog("bug449780_window.xhtml", "bug449780", + "chrome,width=800,height=800,noopener", window); + }); + + ]]></script> +</window> diff --git a/docshell/test/chrome/test_bug453650.xhtml b/docshell/test/chrome/test_bug453650.xhtml new file mode 100644 index 0000000000..2865200478 --- /dev/null +++ b/docshell/test/chrome/test_bug453650.xhtml @@ -0,0 +1,120 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=453650 +--> +<window title="Mozilla Bug 453650" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 453650 **/ + SimpleTest.waitForExplicitFinish(); + + var iter = runTests(); + nextTest(); + + function* runTests() { + var iframe = document.createXULElement("iframe"); + iframe.style.width = "300px"; + iframe.style.height = "300px"; + iframe.setAttribute("src", "data:text/html,<h1 id='h'>hello</h1>"); + + document.documentElement.appendChild(iframe); + yield whenLoaded(iframe); + info("iframe loaded"); + + var h1 = iframe.contentDocument.getElementById("h"); + let myCallback = function() { h1.style.width = "400px"; }; + info("Calling waitForInterruptibleReflow"); + yield waitForInterruptibleReflow(iframe.docShell, myCallback); + info("got past top-level waitForInterruptibleReflow"); + + myCallback = function() { h1.style.width = "300px"; }; + info("Calling waitForReflow"); + waitForReflow(iframe.docShell, myCallback); + info("got past top-level waitForReflow"); + yield is(300, h1.offsetWidth, "h1 has correct width"); + + SimpleTest.finish(); + } + + function waitForInterruptibleReflow(docShell, + callbackThatShouldTriggerReflow) { + waitForReflow(docShell, callbackThatShouldTriggerReflow, true); + } + + function waitForReflow(docShell, callbackThatShouldTriggerReflow, + interruptible = false) { + info("Entering waitForReflow"); + function done() { + info("Entering done (inside of waitForReflow)"); + + docShell.removeWeakReflowObserver(observer); + SimpleTest.executeSoon(nextTest); + } + + var observer = { + reflow (start, end) { + info("Entering observer.reflow"); + if (interruptible) { + ok(false, "expected interruptible reflow"); + } else { + ok(true, "observed uninterruptible reflow"); + } + + info("times: " + start + ", " + end); + ok(start <= end, "reflow start time lower than end time"); + done(); + }, + + reflowInterruptible (start, end) { + info("Entering observer.reflowInterruptible"); + if (!interruptible) { + ok(false, "expected uninterruptible reflow"); + } else { + ok(true, "observed interruptible reflow"); + } + + info("times: " + start + ", " + end); + ok(start <= end, "reflow start time lower than end time"); + done(); + }, + + QueryInterface: ChromeUtils.generateQI([ + "nsIReflowObserver", + "nsISupportsWeakReference", + ]), + }; + + info("waitForReflow is adding a reflow observer"); + docShell.addWeakReflowObserver(observer); + callbackThatShouldTriggerReflow(); + } + + function whenLoaded(iframe) { + info("entering whenLoaded"); + iframe.addEventListener("load", function() { + SimpleTest.executeSoon(nextTest); + }, { once: true }); + } + + function nextTest() { + info("entering nextTest"); + iter.next(); + } + + ]]> + </script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=453650" + target="_blank">Mozilla Bug 453650</a> + </body> +</window> diff --git a/docshell/test/chrome/test_bug454235.xhtml b/docshell/test/chrome/test_bug454235.xhtml new file mode 100644 index 0000000000..ed6dc57c41 --- /dev/null +++ b/docshell/test/chrome/test_bug454235.xhtml @@ -0,0 +1,40 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=454235 +--> +<window title="Mozilla Bug 454235" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=454235" + target="_blank">Mozilla Bug 454235</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + + /** Test for Bug 454235 **/ +SimpleTest.waitForExplicitFinish(); + +SimpleTest.waitForFocus(doTest); + +function doTest() { + var hiddenBrowser = document.getElementById("hiddenBrowser"); + + hiddenBrowser.contentWindow.focus(); + ok(!hiddenBrowser.contentDocument.hasFocus(), "hidden browser is unfocusable"); + + SimpleTest.finish(); +} + + + + ]]></script> + <box flex="1" style="visibility: hidden; border:5px black solid"> + <browser style="border:5px blue solid" id="hiddenBrowser" src="bug454235-subframe.xhtml"/> + </box> +</window> diff --git a/docshell/test/chrome/test_bug456980.xhtml b/docshell/test/chrome/test_bug456980.xhtml new file mode 100644 index 0000000000..d1741209c6 --- /dev/null +++ b/docshell/test/chrome/test_bug456980.xhtml @@ -0,0 +1,29 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=456980 +--> +<window title="Mozilla Bug 456980" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=456980" + target="_blank">Mozilla Bug 396519</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + SimpleTest.waitForExplicitFinish(); + + addLoadEvent(function() { + window.openDialog("bug113934_window.xhtml?chrome", "bug456980", + "chrome,width=800,height=800,noopener", window); + }); + + ]]></script> +</window> diff --git a/docshell/test/chrome/test_bug565388.xhtml b/docshell/test/chrome/test_bug565388.xhtml new file mode 100644 index 0000000000..7919176cba --- /dev/null +++ b/docshell/test/chrome/test_bug565388.xhtml @@ -0,0 +1,79 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=565388 +--> +<window title="Mozilla Bug 565388" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 565388 **/ + SimpleTest.waitForExplicitFinish(); + +function test() { + var progressListener = { + add(docShell, callback) { + this.callback = callback; + this.docShell = docShell; + docShell. + QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIWebProgress). + addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW); + }, + + finish() { + this.docShell. + QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIWebProgress). + removeProgressListener(this); + this.callback(); + }, + + onStateChange (webProgress, req, flags, status) { + if (req.name.startsWith("data:application/vnd.mozilla.xul")) { + if (flags & Ci.nsIWebProgressListener.STATE_STOP) + this.finish(); + } + }, + + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), + } + + var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"] + .getService(Ci.nsIPrincipal); + var webNav = SpecialPowers.Services.appShell.createWindowlessBrowser(true); + var docShell = webNav.docShell; + docShell.createAboutBlankContentViewer(systemPrincipal, systemPrincipal); + var win = docShell.contentViewer.DOMDocument.defaultView; + + progressListener.add(docShell, function(){ + is(win.document.documentURI, "data:application/xhtml+xml;charset=utf-8,<window/>"); + webNav.close(); + SimpleTest.finish(); + }); + + win.location = "data:application/xhtml+xml;charset=utf-8,<window/>"; +} + +addLoadEvent(function onLoad() { + test(); +}); + + ]]> + </script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=565388" + target="_blank">Mozilla Bug 565388</a> + </body> +</window> diff --git a/docshell/test/chrome/test_bug582176.xhtml b/docshell/test/chrome/test_bug582176.xhtml new file mode 100644 index 0000000000..718a500cdc --- /dev/null +++ b/docshell/test/chrome/test_bug582176.xhtml @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=582176.xul +--> +<window title="Mozilla Bug 582176" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=582176"> + Mozilla Bug 582176</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 582176 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug582176_window.xhtml", "bug582176", + "chrome,width=600,height=600,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug608669.xhtml b/docshell/test/chrome/test_bug608669.xhtml new file mode 100644 index 0000000000..182c307766 --- /dev/null +++ b/docshell/test/chrome/test_bug608669.xhtml @@ -0,0 +1,80 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=608669 +--> +<window title="Mozilla Bug 608669" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=608669" + target="_blank">Mozilla Bug 608669</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + +/** Test for Bug 608669 **/ +SimpleTest.waitForExplicitFinish(); + +addLoadEvent(nextTest); + +let gen = doTest(); + +function nextTest() { + gen.next(); +} + +let chromeWindow = window.browsingContext.topChromeWindow; + +function* doTest() { + var notificationCount = 0; + var observer = { + observe(aSubject, aTopic, aData) { + is(aTopic, "chrome-document-global-created", + "correct topic"); + is(aData, "null", + "correct data"); + notificationCount++; + } + }; + + var os = SpecialPowers.Services.obs; + os.addObserver(observer, "chrome-document-global-created"); + os.addObserver(observer, "content-document-global-created"); + + is(notificationCount, 0, "initial count"); + + // create a new window + var testWin = chromeWindow.open("", "bug 608669", "chrome,width=600,height=600"); + testWin.x = "y"; + is(notificationCount, 1, "after created window"); + + // Try loading in the window + testWin.location = "bug608669.xhtml"; + chromeWindow.onmessage = nextTest; + yield undefined; + is(notificationCount, 1, "after first load"); + is(testWin.x, "y", "reused window"); + + // Try loading again in the window + testWin.location = "bug608669.xhtml?x"; + chromeWindow.onmessage = nextTest; + yield undefined; + is(notificationCount, 2, "after second load"); + is("x" in testWin, false, "didn't reuse window"); + + chromeWindow.onmessage = null; + + testWin.close(); + + os.removeObserver(observer, "chrome-document-global-created"); + os.removeObserver(observer, "content-document-global-created"); + SimpleTest.finish(); +} + + ]]></script> +</window> diff --git a/docshell/test/chrome/test_bug662200.xhtml b/docshell/test/chrome/test_bug662200.xhtml new file mode 100644 index 0000000000..5af4e200e2 --- /dev/null +++ b/docshell/test/chrome/test_bug662200.xhtml @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=662200.xul +--> +<window title="Mozilla Bug 662200" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=662200"> + Mozilla Bug 662200</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 662200 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug662200_window.xhtml", "bug662200", + "chrome,width=600,height=600,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug690056.xhtml b/docshell/test/chrome/test_bug690056.xhtml new file mode 100644 index 0000000000..f1b330a3da --- /dev/null +++ b/docshell/test/chrome/test_bug690056.xhtml @@ -0,0 +1,26 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=690056 +--> +<window title="Mozilla Bug 690056" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=690056" + target="_blank">Mozilla Bug 690056</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + /** Test for Bug 690056 **/ +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug690056_window.xhtml", "bug690056", + "chrome,width=600,height=600,noopener", window); + ]]> + </script> +</window> diff --git a/docshell/test/chrome/test_bug789773.xhtml b/docshell/test/chrome/test_bug789773.xhtml new file mode 100644 index 0000000000..0f4a67fc10 --- /dev/null +++ b/docshell/test/chrome/test_bug789773.xhtml @@ -0,0 +1,67 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=789773 +--> +<window title="Mozilla Bug 789773" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=789773" + target="_blank">Mozilla Bug 789773</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + /* Test for Bug 789773. + * + * See comment 50 for the situation we're testing against here. + * + * Note that the failure mode of this test is to hang, and hang the browser on quit. + * This is an unfortunate occurance, but that's why we're testing it. + */ + SimpleTest.waitForExplicitFinish(); + + const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm"); + + var calledListenerForBrowserChromeURL = false; + var testProgressListener = { + START_DOC: Ci.nsIWebProgressListener.STATE_START | Ci.nsIWebProgressListener.STATE_IS_DOCUMENT, + onStateChange(wp, req, stateFlags, status) { + let browserChromeFileName = AppConstants.BROWSER_CHROME_URL.split("/").reverse()[0]; + if (req.name.includes(browserChromeFileName)) { + wp.DOMWindow; // Force the lazy creation of a DOM window. + calledListenerForBrowserChromeURL = true; + } + if (req.name.includes("mozilla.html") && (stateFlags & Ci.nsIWebProgressListener.STATE_STOP)) + finishTest(); + }, + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), + } + + // Add our progress listener + var webProgress = Cc['@mozilla.org/docloaderservice;1'].getService(Ci.nsIWebProgress); + webProgress.addProgressListener(testProgressListener, Ci.nsIWebProgress.NOTIFY_STATE_REQUEST); + + // Open the window. + var popup = window.open("about:mozilla", "_blank", "width=640,height=400"); + + // Wait for the window to load. + function finishTest() { + webProgress.removeProgressListener(testProgressListener); + ok(true, "Loaded the popup window without spinning forever in the event loop!"); + ok(calledListenerForBrowserChromeURL, "Should have called the progress listener for browser.xhtml"); + popup.close(); + SimpleTest.finish(); + } + + ]]> + </script> +</window> diff --git a/docshell/test/chrome/test_bug846906.xhtml b/docshell/test/chrome/test_bug846906.xhtml new file mode 100644 index 0000000000..0fdf0e5079 --- /dev/null +++ b/docshell/test/chrome/test_bug846906.xhtml @@ -0,0 +1,94 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=846906 +--> +<window title="Mozilla Bug 846906" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 846906 **/ + SimpleTest.waitForExplicitFinish(); + + var appShellService = SpecialPowers.Services.appShell; + ok(appShellService, "Should be able to get app shell service"); + + var windowlessBrowser = appShellService.createWindowlessBrowser(); + ok(windowlessBrowser, "Should be able to create windowless browser"); + + ok(windowlessBrowser instanceof Ci.nsIWindowlessBrowser, + "Windowless browser should implement nsIWindowlessBrowser"); + + var webNavigation = windowlessBrowser.QueryInterface(Ci.nsIWebNavigation); + ok(webNavigation, "Windowless browser should implement nsIWebNavigation"); + + var interfaceRequestor = windowlessBrowser.QueryInterface(Ci.nsIInterfaceRequestor); + ok(interfaceRequestor, "Should be able to query interface requestor interface"); + + var docShell = windowlessBrowser.docShell; + ok(docShell, "Should be able to get doc shell interface"); + + var document = webNavigation.document; + ok(document, "Should be able to get document"); + + var iframe = document.createXULElement("iframe"); + ok(iframe, "Should be able to create iframe"); + + iframe.onload = function () { + ok(true, "Should receive initial onload event"); + + iframe.onload = function () { + ok(true, "Should receive onload event"); + + var contentDocument = iframe.contentDocument; + ok(contentDocument, "Should be able to get content document"); + + var div = contentDocument.getElementById("div1"); + ok(div, "Should be able to get element by id"); + + var rect = div.getBoundingClientRect(); + ok(rect, "Should be able to get bounding client rect"); + + // xxx: can we do better than hardcoding these values here? + is(rect.width, 1024); + is(rect.height, 768); + + windowlessBrowser.close(); + + // Once the browser is closed, nsIWebNavigation and + // nsIInterfaceRequestor methods should no longer be accessible. + try { + windowlessBrowser.getInterface(Ci.nsIDocShell); + ok(false); + } catch (e) { + is(e.result, Cr.NS_ERROR_NULL_POINTER); + } + + try { + windowlessBrowser.document; + ok(false); + } catch (e) { + is(e.result, Cr.NS_ERROR_NULL_POINTER); + } + + SimpleTest.finish(); + }; + iframe.setAttribute("src", "http://mochi.test:8888/chrome/docshell/test/chrome/bug846906.html"); + }; + document.documentElement.appendChild(iframe); + + ]]> + </script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=846906" + target="_blank">Mozilla Bug 846906</a> + </body> +</window> diff --git a/docshell/test/chrome/test_bug89419.xhtml b/docshell/test/chrome/test_bug89419.xhtml new file mode 100644 index 0000000000..6a00080aa6 --- /dev/null +++ b/docshell/test/chrome/test_bug89419.xhtml @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=89419.xul +--> +<window title="Mozilla Bug 89419" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=89419"> + Mozilla Bug 89419</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 89419 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug89419_window.xhtml", "bug89419", + "chrome,width=600,height=600,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_bug909218.html b/docshell/test/chrome/test_bug909218.html new file mode 100644 index 0000000000..bcbcc176eb --- /dev/null +++ b/docshell/test/chrome/test_bug909218.html @@ -0,0 +1,117 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); +addLoadEvent(test); + +// The default flags we will stick on the docShell - every request made by the +// docShell should include those flags. +const TEST_FLAGS = Ci.nsIRequest.LOAD_ANONYMOUS | + Ci.nsIRequest.LOAD_BYPASS_CACHE | + Ci.nsIRequest.INHIBIT_CACHING; + +var TEST_URL = "http://mochi.test:8888/chrome/docshell/test/chrome/bug909218.html"; + +// These are the requests we expect to see loading TEST_URL into our iframe. + +// The test entry-point. The basic outline is: +// * Create an iframe and set defaultLoadFlags on its docShell. +// * Add a web progress listener to observe each request as the iframe is +// loaded, and check that each request has the flags we specified. +// * Load our test URL into the iframe and wait for the load to complete. +function test() { + var iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + var docShell = iframe.contentWindow.docShell; + // Add our progress listener - when it notices the top-level document is + // complete, the test will end. + RequestWatcher.init(docShell, SimpleTest.finish); + // Set the flags we care about, then load our test URL. + docShell.defaultLoadFlags = TEST_FLAGS; + iframe.setAttribute("src", TEST_URL); +} + +// an nsIWebProgressListener that checks all requests made by the docShell +// have the flags we expect. +var RequestWatcher = { + init(docShell, callback) { + this.callback = callback; + this.docShell = docShell; + docShell. + QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIWebProgress). + addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_REQUEST | + Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); + // These are the requests we expect to see - initialize each to have a + // count of zero. + this.requestCounts = {}; + for (var url of [ + TEST_URL, + // content loaded by the above test html. + "http://mochi.test:8888/chrome/docshell/test/chrome/bug909218.js", + "http://mochi.test:8888/tests/SimpleTest/test.css", + "http://mochi.test:8888/tests/docshell/test/chrome/red.png", + // the content of an iframe in the test html. + "http://mochi.test:8888/chrome/docshell/test/chrome/generic.html", + ]) { + this.requestCounts[url] = 0; + } + }, + + // Finalize the test after we detect a completed load. We check we saw the + // correct requests and make a callback to exit. + finalize() { + ok(Object.keys(this.requestCounts).length, "we expected some requests"); + for (var url in this.requestCounts) { + var count = this.requestCounts[url]; + // As we are looking at all request states, we expect more than 1 for + // each URL - 0 or 1 would imply something went wrong - >1 just means + // multiple states for each request were recorded, which we don't care + // about (we do care they all have the correct flags though - but we + // do that in onStateChange) + ok(count > 1, url + " saw " + count + " requests"); + } + this.docShell. + QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIWebProgress). + removeProgressListener(this); + this.callback(); + }, + + onStateChange(webProgress, req, flags, status) { + // We are checking requests - if there isn't one, ignore it. + if (!req) { + return; + } + // We will usually see requests for 'about:document-onload-blocker' not + // have the flag, so we just ignore them. + // We also see, eg, resource://gre-resources/loading-image.png, so + // skip resource:// URLs too. + // We may also see, eg, chrome://global/skin/icons/chevron.svg, so + // skip chrome:// URLs too. + if (req.name.startsWith("about:") || req.name.startsWith("resource:") || + req.name.startsWith("chrome:") || req.name.startsWith("documentchannel:")) { + return; + } + is(req.loadFlags & TEST_FLAGS, TEST_FLAGS, "request " + req.name + " has the expected flags"); + this.requestCounts[req.name] += 1; + var stopFlags = Ci.nsIWebProgressListener.STATE_STOP | + Ci.nsIWebProgressListener.STATE_IS_DOCUMENT; + if (req.name == TEST_URL && (flags & stopFlags) == stopFlags) { + this.finalize(); + } + }, + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), +}; + +</script> +</head> +</html> diff --git a/docshell/test/chrome/test_bug92598.xhtml b/docshell/test/chrome/test_bug92598.xhtml new file mode 100644 index 0000000000..9c3a46866a --- /dev/null +++ b/docshell/test/chrome/test_bug92598.xhtml @@ -0,0 +1,37 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=92598 +--> +<window title="Mozilla Bug 92598" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=92598">Mozilla Bug 92598</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 92598 **/ + +SimpleTest.waitForExplicitFinish(); +window.openDialog("bug92598_window.xhtml", "bug92598", + "chrome,width=600,height=600,noopener", window); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_docRedirect.sjs b/docshell/test/chrome/test_docRedirect.sjs new file mode 100644 index 0000000000..4050eb06d7 --- /dev/null +++ b/docshell/test/chrome/test_docRedirect.sjs @@ -0,0 +1,6 @@ +function handleRequest(request, response) { + response.setStatusLine(request.httpVersion, 301, "Moved Permanently"); + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + response.setHeader("Location", "http://example.org/"); + response.write("Hello world!"); +} diff --git a/docshell/test/chrome/test_docRedirect.xhtml b/docshell/test/chrome/test_docRedirect.xhtml new file mode 100644 index 0000000000..1688d9823e --- /dev/null +++ b/docshell/test/chrome/test_docRedirect.xhtml @@ -0,0 +1,91 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1342989 +--> +<window title="Mozilla Bug 1342989" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <script type="application/javascript"> + <![CDATA[ + SimpleTest.waitForExplicitFinish(); + + const WEB_PROGRESS_LISTENER_FLAGS = + Object.keys(Ci.nsIWebProgressListener).filter( + propName => propName.startsWith("STATE_") + ); + + function bitFlagsToNames(flags, knownNames, intf) { + return knownNames.map( (F) => { + return (flags & intf[F]) ? F : undefined; + }).filter( (s) => !!s ); + } + + var progressListener = { + add(docShell, callback) { + this.callback = callback; + this.docShell = docShell; + docShell. + QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIWebProgress). + addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_ALL); + }, + + finish(success) { + this.docShell. + QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIWebProgress). + removeProgressListener(this); + this.callback(success); + }, + + onStateChange (webProgress, req, flags, status) { + if (!(flags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) && + !(flags & Ci.nsIWebProgressListener.STATE_IS_REDIRECTED_DOCUMENT)) + return; + + var channel = req.QueryInterface(Ci.nsIChannel); + + if (flags & Ci.nsIWebProgressListener.STATE_IS_REDIRECTED_DOCUMENT) { + SimpleTest.is(channel.URI.host, "example.org", + "Should be redirected to example.org (see test_docRedirect.sjs)"); + this.finish(true); + } + + // Fail in case we didn't receive document redirection event. + if (flags & Ci.nsIWebProgressListener.STATE_STOP) + this.finish(false); + }, + + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), + } + + var webNav = SpecialPowers.Services.appShell.createWindowlessBrowser(true); + let docShell = webNav.docShell; + let system = Cc["@mozilla.org/systemprincipal;1"].getService(Ci.nsIPrincipal); + docShell.createAboutBlankContentViewer(system, system); + + progressListener.add(docShell, function(success) { + webNav.close(); + SimpleTest.is(success, true, "Received document redirect event"); + SimpleTest.finish(); + }); + + var win = docShell.contentViewer.DOMDocument.defaultView; + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + win.location = "http://example.com/chrome/docshell/test/chrome/test_docRedirect.sjs" + + ]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1342989" + target="_blank">Mozilla Bug 1342989</a> + </body> +</window> diff --git a/docshell/test/chrome/test_mozFrameType.xhtml b/docshell/test/chrome/test_mozFrameType.xhtml new file mode 100644 index 0000000000..6e8bfd8f85 --- /dev/null +++ b/docshell/test/chrome/test_mozFrameType.xhtml @@ -0,0 +1,42 @@ +<?xml version="1.0"?> +<!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=769771 +--> +<window title="Test mozFrameType attribute" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +if (navigator.platform.startsWith("Win")) { + SimpleTest.expectAssertions(0, 1); +} + +/** Test for Bug 769771 **/ +SimpleTest.waitForExplicitFinish(); + +addLoadEvent(function () { + window.openDialog("mozFrameType_window.xhtml", "mozFrameType", + "chrome,width=600,height=600,noopener", window); +}); + +]]> +</script> + +</window> diff --git a/docshell/test/chrome/test_open_and_immediately_close_opener.html b/docshell/test/chrome/test_open_and_immediately_close_opener.html new file mode 100644 index 0000000000..bb5f6f054d --- /dev/null +++ b/docshell/test/chrome/test_open_and_immediately_close_opener.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test for Bug 1702678</title> + <meta charset="utf-8"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1702678">Mozilla Bug 1702678</a> + +<script type="application/javascript"> +"use strict"; + +const HTML = ` +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <script> + // We need to queue a promise reaction job whilch will close the window + // during the nested event loop spun up by the window opening code. + Promise.resolve().then(() => { + window.close(); + }); + window.open("data:text/html,Hello"); + <\/script> +</head> +</html> +`; + +add_task(async function() { + // This bug only manifests when opening tabs in new windows. + await SpecialPowers.pushPrefEnv({ + set: [["browser.link.open_newwindow", 2]], + }); + + // Create a window in a new BrowsingContextGroup so that it will be the last + // window in the group when it closes, and the group will be destroyed. + window.open(`data:text/html,${encodeURIComponent(HTML)}`, "", "noopener"); + + // Make a few trips through the event loop to ensure we've had a chance to + // open and close the relevant windows. + for (let i = 0; i < 10; i++) { + await new Promise(resolve => setTimeout(resolve, 0)); + } + + ok(true, "We didn't crash"); +}); +</script> + +</body> +</html> + diff --git a/docshell/test/chrome/test_viewsource_forbidden_in_iframe.xhtml b/docshell/test/chrome/test_viewsource_forbidden_in_iframe.xhtml new file mode 100644 index 0000000000..0cc45c7821 --- /dev/null +++ b/docshell/test/chrome/test_viewsource_forbidden_in_iframe.xhtml @@ -0,0 +1,159 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin/global.css"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=624883 +--> +<window title="Mozilla Bug 624883" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=624883" + target="_blank">Mozilla Bug 624883</a> + </body> + + <!-- test code goes here --> + <iframe type="content" onload="startTest()" src="file_viewsource_forbidden_in_iframe.html"></iframe> + + <script type="application/javascript"> + <![CDATA[ + + SimpleTest.waitForExplicitFinish(); + + // We create a promise that will resolve with the error message + // on a network error page load and reject on any other load. + function createNetworkErrorMessagePromise(frame) { + return new Promise(function(resolve, reject) { + + // Error pages do not fire "load" events, so use a progressListener. + var originalDocumentURI = frame.contentDocument.documentURI; + var progressListener = { + onLocationChange(aWebProgress, aRequest, aLocation, aFlags) { + // Make sure nothing other than an error page is loaded. + if (!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE)) { + reject("location change was not to an error page"); + } + }, + + onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) { + // Wait until the documentURI changes (from about:blank) this should + // be the error page URI. + var documentURI = frame.contentDocument.documentURI; + if (documentURI == originalDocumentURI) { + return; + } + + aWebProgress.removeProgressListener(progressListener, + Ci.nsIWebProgress.NOTIFY_ALL); + var matchArray = /about:neterror\?.*&d=([^&]*)/.exec(documentURI); + if (!matchArray) { + reject("no network error message found in URI") + return; + } + + var errorMsg = matchArray[1]; + resolve(decodeURIComponent(errorMsg)); + }, + + QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener", + "nsISupportsWeakReference"]) + }; + + frame.contentWindow.docShell + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebProgress) + .addProgressListener(progressListener, + Ci.nsIWebProgress.NOTIFY_LOCATION | + Ci.nsIWebProgress.NOTIFY_STATE_REQUEST); + }); + } + + function startTest() { + // Get a reference message that we know will be an unknown protocol message, + // so we can use it for comparisons in the test cases. + var refIframe = window[0].document.getElementById("refIframe"); + var refErrorPromise = createNetworkErrorMessagePromise(refIframe); + + refErrorPromise.then( + function(msg) { + window.refErrorMsg = msg; + var testIframe = window[0].document.getElementById("testIframe"); + + // Run test cases on load of "about:blank", so that the URI always changes + // and we can detect this in our Promise. + testIframe.onload = runNextTestCase; + testIframe.src = "about:blank"; + }, + function(reason) { + ok(false, "Could not get reference error message", reason); + SimpleTest.finish(); + }) + .catch(function(e) { + ok(false, "Unexpected exception thrown getting reference error message", e); + }); + + refIframe.src = "wibble://example.com"; + } + + function runTestCase(testCase) { + var testIframe = window[0].document.getElementById("testIframe"); + var expectedErrorMsg = window.refErrorMsg.replace("wibble", testCase.expectedProtocolList); + + var testErrorPromise = createNetworkErrorMessagePromise(testIframe); + testErrorPromise.then( + function(actualErrorMsg) { + is(actualErrorMsg, expectedErrorMsg, testCase.desc); + testIframe.src = "about:blank"; + }, + function(reason) { + ok(false, testCase.desc, reason); + testIframe.src = "about:blank"; + }) + .catch(function(e) { + ok(false, testCase.desc + " - unexpected exception thrown", e); + }); + + testIframe.src = testCase.protocols + "://example.com/!/"; + } + + var testCaseIndex = -1; + let testCases = [ + { + desc: "Test 1: view-source should not be allowed in an iframe", + protocols: "view-source:http", + expectedProtocolList: "view-source, http" + }, + { + desc: "Test 2: jar:view-source should not be allowed in an iframe", + protocols: "jar:view-source:http", + expectedProtocolList: "jar, view-source, http" + }, + { + desc: "Test 3: if invalid protocol first should report before view-source", + protocols: "wibble:view-source:http", + // Nothing after the invalid protocol gets set as a proper nested URI, + // so the list stops there. + expectedProtocolList: "wibble" + }, + { + desc: "Test 4: if view-source first should report before invalid protocol", + protocols: "view-source:wibble:http", + expectedProtocolList: "view-source, wibble" + } + ]; + + function runNextTestCase() { + ++testCaseIndex; + if (testCaseIndex == testCases.length) { + SimpleTest.finish(); + return; + } + + runTestCase(testCases[testCaseIndex]); + } + + ]]> + </script> +</window> diff --git a/docshell/test/chrome/window.template.txt b/docshell/test/chrome/window.template.txt new file mode 100644 index 0000000000..4c520dc075 --- /dev/null +++ b/docshell/test/chrome/window.template.txt @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="{BUGNUMBER}Test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="600" + height="600" + onload="setTimeout(nextTest,0);" + title="bug {BUGNUMBER} test"> + + <script type="application/javascript" + src="docshell_helpers.js"> + </script> + + <script type="application/javascript"><![CDATA[ + + // Define the generator-iterator for the tests. + var tests = testIterator(); + + //// + // Execute the next test in the generator function. + // + function nextTest() { + tests.next(); + } + + //// + // Generator function for test steps for bug {BUGNUMBER}: + // Description goes here. + // + function testIterator() + { + // Test steps go here. See bug303267_window.xhtml for an example. + + // Tell the framework the test is finished. Include the final 'yield' + // statement to prevent a StopIteration exception from being thrown. + finish(); + yield undefined; + } + + ]]></script> + + <browser type="content" primary="true" flex="1" id="content" src="about:blank"/> +</window> diff --git a/docshell/test/iframesandbox/file_child_navigation_by_location.html b/docshell/test/iframesandbox/file_child_navigation_by_location.html new file mode 100644 index 0000000000..7365bed81f --- /dev/null +++ b/docshell/test/iframesandbox/file_child_navigation_by_location.html @@ -0,0 +1 @@ +<script>function onNav() { parent.parent.postMessage("childIframe", "*"); } window.onload = onNav; window.onhashchange = onNav;</script> diff --git a/docshell/test/iframesandbox/file_marquee_event_handlers.html b/docshell/test/iframesandbox/file_marquee_event_handlers.html new file mode 100644 index 0000000000..13ee31ddb7 --- /dev/null +++ b/docshell/test/iframesandbox/file_marquee_event_handlers.html @@ -0,0 +1,17 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Test marquee attribute event handlers in iframe sandbox</title> +</head> +<body> + <!-- Note that the width here is slightly longer than the contents, to make + sure we bounce and finish very quickly. --> + <marquee loop="2" width="145" behavior="alternate" truespeed scrolldelay="1" + onstart="parent.postMessage(window.name + ' marquee onstart', '*');" + onbounce="parent.postMessage(window.name + ' marquee onbounce', '*');" + onfinish="parent.postMessage(window.name + ' marquee onfinish', '*');"> + Will bounce and finish + </marquee> +</body> +</html> diff --git a/docshell/test/iframesandbox/file_other_auxiliary_navigation_by_location.html b/docshell/test/iframesandbox/file_other_auxiliary_navigation_by_location.html new file mode 100644 index 0000000000..ad24c0f242 --- /dev/null +++ b/docshell/test/iframesandbox/file_other_auxiliary_navigation_by_location.html @@ -0,0 +1,15 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Test window for other auxiliary navigation by location tests</title> +<script> + function onNav() { + opener.postMessage(window.name, "*"); + } + + window.onload = onNav; + window.onhashchange = onNav; +</script> +</head> +</html> diff --git a/docshell/test/iframesandbox/file_our_auxiliary_navigation_by_location.html b/docshell/test/iframesandbox/file_our_auxiliary_navigation_by_location.html new file mode 100644 index 0000000000..978980df25 --- /dev/null +++ b/docshell/test/iframesandbox/file_our_auxiliary_navigation_by_location.html @@ -0,0 +1,15 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Test window for our auxiliary navigation by location tests</title> +<script> + function onNav() { + opener.parent.postMessage(window.name, "*"); + } + + window.onload = onNav; + window.onhashchange = onNav; +</script> +</head> +</html> diff --git a/docshell/test/iframesandbox/file_parent_navigation_by_location.html b/docshell/test/iframesandbox/file_parent_navigation_by_location.html new file mode 100644 index 0000000000..9a2e95fad0 --- /dev/null +++ b/docshell/test/iframesandbox/file_parent_navigation_by_location.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Test window for parent navigation by location tests</title> +<script> + function onNav() { + parent.postMessage(window.name, "*"); + } + + window.onload = onNav; + window.onhashchange = onNav; +</script> +</head> +<body> + <iframe name="childIframe" sandbox="allow-scripts allow-same-origin allow-top-navigation"></iframe> +</body> +</html> diff --git a/docshell/test/iframesandbox/file_sibling_navigation_by_location.html b/docshell/test/iframesandbox/file_sibling_navigation_by_location.html new file mode 100644 index 0000000000..51a52bb8eb --- /dev/null +++ b/docshell/test/iframesandbox/file_sibling_navigation_by_location.html @@ -0,0 +1,15 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Test window for sibling navigation by location tests</title> +<script> + function onNav() { + parent.postMessage(window.name, "*"); + } + + window.onload = onNav; + window.onhashchange = onNav; +</script> +</head> +</html> diff --git a/docshell/test/iframesandbox/file_top_navigation_by_location.html b/docshell/test/iframesandbox/file_top_navigation_by_location.html new file mode 100644 index 0000000000..194430f38c --- /dev/null +++ b/docshell/test/iframesandbox/file_top_navigation_by_location.html @@ -0,0 +1,20 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Test window for top navigation by location tests</title> +<script> + function onNav() { + opener.postMessage(window.name, "*"); + } + + window.onload = onNav; + window.onhashchange = onNav; +</script> +</head> +<body> + <iframe name="if1" sandbox="allow-scripts allow-same-origin"></iframe> + <iframe name="if2" sandbox="allow-scripts allow-same-origin allow-top-navigation"></iframe> + <iframe name="if3" sandbox="allow-scripts allow-top-navigation"></iframe> +</body> +</html> diff --git a/docshell/test/iframesandbox/file_top_navigation_by_location_exotic.html b/docshell/test/iframesandbox/file_top_navigation_by_location_exotic.html new file mode 100644 index 0000000000..9a26bc80db --- /dev/null +++ b/docshell/test/iframesandbox/file_top_navigation_by_location_exotic.html @@ -0,0 +1,27 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Test window for top navigation by location tests</title> +<script> + function onBlock() { + opener.postMessage({ name: window.name, blocked: true }, "*"); + } + + function onNav() { + opener.postMessage({ name: window.name, blocked: false }, "*"); + } + + function setOwnHref() { + // eslint-disable-next-line no-self-assign + location.href = location.href; + } + + window.onload = onNav; +</script> +</head> +<body> + <iframe name="if1" sandbox="allow-scripts allow-same-origin"></iframe> + <iframe name="if2" sandbox="allow-scripts allow-same-origin allow-top-navigation"></iframe> +</body> +</html> diff --git a/docshell/test/iframesandbox/file_top_navigation_by_user_activation.html b/docshell/test/iframesandbox/file_top_navigation_by_user_activation.html new file mode 100644 index 0000000000..8454f1fac4 --- /dev/null +++ b/docshell/test/iframesandbox/file_top_navigation_by_user_activation.html @@ -0,0 +1,27 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Test window for top navigation with user activation</title> +<script> +window.onload = () => { + opener.postMessage("READY", "*"); +}; + +window.onhashchange = () => { + opener.postMessage("NAVIGATED", "*"); +}; + +window.onmessage = (e) => { + if (e.data == "CLICK" || e.data == "SCRIPT") { + frames[0].postMessage([e.data, location.href + "#hash"], "*"); + } else { + opener.postMessage(e.data, "*"); + } +}; +</script> +</head> +<body> + <iframe sandbox="allow-scripts allow-top-navigation-by-user-activation" src="http://example.org/tests/docshell/test/iframesandbox/file_top_navigation_by_user_activation_iframe.html"></iframe> +</body> +</html> diff --git a/docshell/test/iframesandbox/file_top_navigation_by_user_activation_iframe.html b/docshell/test/iframesandbox/file_top_navigation_by_user_activation_iframe.html new file mode 100644 index 0000000000..b775579f28 --- /dev/null +++ b/docshell/test/iframesandbox/file_top_navigation_by_user_activation_iframe.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<title>Test window for top navigation with user activation</title> +<script> +function navigate(aURL) { + try { + top.location.href = aURL; + } catch (e) { + top.postMessage("BLOCKED", "*"); + } +} + +window.onmessage = (e) => { + SpecialPowers.wrap(document).clearUserGestureActivation(); + let [command, url] = e.data; + if (command == "CLICK") { + let button = document.querySelector("button"); + button.addEventListener("click", () => { + navigate(url); + }, { once: true }); + synthesizeMouseAtCenter(button, {}); + } else if (command == "SCRIPT") { + navigate(url); + } +}; +</script> +</head> +<body><button>Click</button></body> +</html> diff --git a/docshell/test/iframesandbox/mochitest.ini b/docshell/test/iframesandbox/mochitest.ini new file mode 100644 index 0000000000..4b6b1d8ecc --- /dev/null +++ b/docshell/test/iframesandbox/mochitest.ini @@ -0,0 +1,28 @@ +[DEFAULT] +support-files = + file_child_navigation_by_location.html + file_marquee_event_handlers.html + file_other_auxiliary_navigation_by_location.html + file_our_auxiliary_navigation_by_location.html + file_parent_navigation_by_location.html + file_sibling_navigation_by_location.html + file_top_navigation_by_location.html + file_top_navigation_by_location_exotic.html + +[test_child_navigation_by_location.html] +[test_marquee_event_handlers.html] +skip-if = true # Bug 1455996 +[test_other_auxiliary_navigation_by_location.html] +tags = openwindow +[test_our_auxiliary_navigation_by_location.html] +tags = openwindow +[test_parent_navigation_by_location.html] +tags = openwindow +[test_sibling_navigation_by_location.html] +tags = openwindow +[test_top_navigation_by_location_exotic.html] +[test_top_navigation_by_location.html] +[test_top_navigation_by_user_activation.html] +support-files = + file_top_navigation_by_user_activation.html + file_top_navigation_by_user_activation_iframe.html diff --git a/docshell/test/iframesandbox/test_child_navigation_by_location.html b/docshell/test/iframesandbox/test_child_navigation_by_location.html new file mode 100644 index 0000000000..383320a02b --- /dev/null +++ b/docshell/test/iframesandbox/test_child_navigation_by_location.html @@ -0,0 +1,91 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=785310 +html5 sandboxed iframe should not be able to perform top navigation with scripts allowed +--> +<head> +<meta charset="utf-8"> +<title>Test for Bug 785310 - iframe sandbox child navigation by location tests</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + +<script> + SimpleTest.waitForExplicitFinish(); + + var testDataUri = "file_child_navigation_by_location.html"; + + function runScriptNavigationTest(testCase) { + window.onmessage = function(event) { + if (event.data != "childIframe") { + ok(false, "event.data: got '" + event.data + "', expected 'childIframe'"); + } + ok(!testCase.shouldBeBlocked, testCase.desc + " - child navigation was NOT blocked"); + runNextTest(); + }; + try { + window.parentIframe.eval(testCase.script); + } catch (e) { + ok(testCase.shouldBeBlocked, testCase.desc + " - " + e.message); + runNextTest(); + } + } + + var testCaseIndex = -1; + var testCases = [ + { + desc: "Test 1: cross origin child location.replace should NOT be blocked", + script: "window['crossOriginChildIframe'].location.replace(\"" + testDataUri + "\")", + shouldBeBlocked: false, + }, + { + desc: "Test 2: cross origin child location.assign should be blocked", + script: "window['crossOriginChildIframe'].location.assign(\"" + testDataUri + "\")", + shouldBeBlocked: true, + }, + { + desc: "Test 3: same origin child location.assign should NOT be blocked", + script: "window['sameOriginChildIframe'].location.assign(\"" + testDataUri + "\")", + shouldBeBlocked: false, + }, + { + desc: "Test 4: cross origin child location.href should NOT be blocked", + script: "window['crossOriginChildIframe'].location.href = \"" + testDataUri + "\"", + shouldBeBlocked: false, + }, + { + desc: "Test 5: cross origin child location.hash should be blocked", + script: "window['crossOriginChildIframe'].location.hash = 'wibble'", + shouldBeBlocked: true, + }, + { + desc: "Test 6: same origin child location.hash should NOT be blocked", + script: "window['sameOriginChildIframe'].location.hash = 'wibble'", + shouldBeBlocked: false, + }, + ]; + + function runNextTest() { + ++testCaseIndex; + if (testCaseIndex == testCases.length) { + SimpleTest.finish(); + return; + } + + runScriptNavigationTest(testCases[testCaseIndex]); + } + + addLoadEvent(runNextTest); +</script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a> +<p id="display"></p> +<div id="content"> +Tests for Bug 785310 +</div> + +<iframe name="parentIframe" sandbox="allow-scripts allow-same-origin" srcdoc="<iframe name='sameOriginChildIframe'></iframe><iframe name='crossOriginChildIframe' sandbox='allow-scripts'></iframe>"</iframe> + +</body> +</html> diff --git a/docshell/test/iframesandbox/test_marquee_event_handlers.html b/docshell/test/iframesandbox/test_marquee_event_handlers.html new file mode 100644 index 0000000000..80added8ab --- /dev/null +++ b/docshell/test/iframesandbox/test_marquee_event_handlers.html @@ -0,0 +1,95 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1277475 +html5 sandboxed iframe should not run marquee attribute event handlers without allow-scripts +--> +<head> +<meta charset="utf-8"> +<title>Test for Bug 1277475 - html5 sandboxed iframe should not run marquee attribute event handlers without allow-scripts</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=1277475">Mozilla Bug 1277475</a> +<p id="display"></p> +<div id="content">Tests for Bug 1277475</div> + +<iframe id="if1" name="if1" src="file_marquee_event_handlers.html" + sandbox="allow-same-origin allow-forms allow-top-navigation allow-pointer-lock allow-orientation-lock allow-popups allow-modals allow-popups-to-escape-sandbox"> +</iframe> + +<iframe id="if2" name="if2" src="file_marquee_event_handlers.html" + sandbox="allow-scripts"></iframe> + +<script> + SimpleTest.waitForExplicitFinish(); + + var expectedMessages = new Set(); + var numberOfMessagesExpected = 4; + var unexpectedMessages = new Set(); + + window.onmessage = function(event) { + info(event.data + " message received"); + if (event.data.startsWith("if2") || event.data == "if1 chaser") { + expectedMessages.add(event.data); + if (expectedMessages.size == numberOfMessagesExpected) { + checkRecievedMessages(); + } + } else { + unexpectedMessages.add(event.data); + } + }; + + function checkRecievedMessages() { + // Check the expected messages explicitly as a cross-check. + ok(expectedMessages.has("if1 chaser"), + "if1 chaser message should have been received"); + ok(expectedMessages.has("if2 marquee onstart"), + "if2 marquee onstart should have run in iframe sandbox with allow-scripts"); + ok(expectedMessages.has("if2 marquee onbounce"), + "if2 marquee onbounce should have run in iframe sandbox with allow-scripts"); + ok(expectedMessages.has("if2 marquee onfinish"), + "if2 marquee onfinish should have run in iframe sandbox with allow-scripts"); + + unexpectedMessages.forEach( + (v) => { + ok(false, v + " should NOT have run in iframe sandbox without allow-scripts"); + } + ); + + SimpleTest.finish(); + } + + // If things are working properly the attribute event handlers won't run on + // the marquee in if1, so add our own capturing listeners on its window, so we + // know when they have fired. (These will run as we are not sandboxed.) + var if1FiredEvents = new Set(); + var if1NumberOfEventsExpected = 3; + var if1Win = document.getElementById("if1").contentWindow; + if1Win.addEventListener("start", () => { checkMarqueeEvent("start"); }, true); + if1Win.addEventListener("bounce", () => { checkMarqueeEvent("bounce"); }, true); + if1Win.addEventListener("finish", () => { checkMarqueeEvent("finish"); }, true); + + function checkMarqueeEvent(eventType) { + info("if1 event " + eventType + " fired"); + if1FiredEvents.add(eventType); + if (if1FiredEvents.size == if1NumberOfEventsExpected) { + // Only send the chasing message after a tick of the event loop to allow + // event handlers on the marquee to process. + SimpleTest.executeSoon(sendChasingMessage); + } + } + + function sendChasingMessage() { + // Add our own message listener to if1's window and echo back a chasing + // message to make sure that any messages from incorrectly run marquee + // attribute event handlers should have arrived before it. + if1Win.addEventListener("message", + (e) => { if1Win.parent.postMessage(e.data, "*"); }); + if1Win.postMessage("if1 chaser", "*"); + info("if1 chaser message sent"); + } +</script> +</body> +</html> diff --git a/docshell/test/iframesandbox/test_other_auxiliary_navigation_by_location.html b/docshell/test/iframesandbox/test_other_auxiliary_navigation_by_location.html new file mode 100644 index 0000000000..3440878db7 --- /dev/null +++ b/docshell/test/iframesandbox/test_other_auxiliary_navigation_by_location.html @@ -0,0 +1,80 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=785310 +html5 sandboxed iframe should not be able to perform top navigation with scripts allowed +--> +<head> +<meta charset="utf-8"> +<title>Test for Bug 785310 - iframe sandbox other auxiliary navigation by location tests</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + +<script> + SimpleTest.waitForExplicitFinish(); + + function runScriptNavigationTest(testCase) { + window.onmessage = function(event) { + if (event.data != "otherWindow") { + ok(false, "event.data: got '" + event.data + "', expected 'otherWindow'"); + } + ok(false, testCase.desc + " - auxiliary navigation was NOT blocked"); + runNextTest(); + }; + try { + window.testIframe.eval(testCase.script); + } catch (e) { + ok(true, testCase.desc + " - " + e.message); + runNextTest(); + } + } + + var testCaseIndex = -1; + var testCases = [ + { + desc: "Test 1: location.replace on auxiliary NOT opened by us should be blocked", + script: "parent.openedWindow.location.replace('file_other_auxiliary_navigation_by_location.html')", + }, + { + desc: "Test 2: location.assign on auxiliary NOT opened by us should be blocked", + script: "parent.openedWindow.location.assign('file_other_auxiliary_navigation_by_location.html')", + }, + { + desc: "Test 3: location.href on auxiliary NOT opened by us should be blocked", + script: "parent.openedWindow.location.href = 'file_other_auxiliary_navigation_by_location.html'", + }, + { + desc: "Test 4: location.hash on auxiliary NOT opened by us should be blocked", + script: "parent.openedWindow.location.hash = 'wibble'", + }, + ]; + + function runNextTest() { + ++testCaseIndex; + if (testCaseIndex == testCases.length) { + window.openedWindow.close(); + SimpleTest.finish(); + return; + } + + runScriptNavigationTest(testCases[testCaseIndex]); + } + + window.onmessage = runNextTest; + + window.onload = function() { + window.openedWindow = window.open("file_other_auxiliary_navigation_by_location.html", "otherWindow"); + }; +</script> + +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a> +<p id="display"></p> +<div id="content"> +Tests for Bug 785310 +</div> + +<iframe name="testIframe" sandbox="allow-scripts allow-same-origin allow-top-navigation allow-popups"></iframe> +</body> +</html> diff --git a/docshell/test/iframesandbox/test_our_auxiliary_navigation_by_location.html b/docshell/test/iframesandbox/test_our_auxiliary_navigation_by_location.html new file mode 100644 index 0000000000..1719f566a5 --- /dev/null +++ b/docshell/test/iframesandbox/test_our_auxiliary_navigation_by_location.html @@ -0,0 +1,84 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=785310 +html5 sandboxed iframe should not be able to perform top navigation with scripts allowed +--> +<head> +<meta charset="utf-8"> +<title>Test for Bug 785310 - iframe sandbox our auxiliary navigation by location tests</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + +<script> + SimpleTest.waitForExplicitFinish(); + + function runScriptNavigationTest(testCase) { + window.onmessage = function(event) { + if (event.data != "ourWindow") { + ok(false, "event.data: got '" + event.data + "', expected 'ourWindow'"); + } + ok(!testCase.shouldBeBlocked, testCase.desc + " - auxiliary navigation was NOT blocked"); + runNextTest(); + }; + try { + SpecialPowers.wrap(window.testIframe).eval(testCase.script); + } catch (e) { + ok(testCase.shouldBeBlocked, testCase.desc + " - " + SpecialPowers.wrap(e).message); + runNextTest(); + } + } + + var testCaseIndex = -1; + var testCases = [ + { + desc: "Test 1: location.replace on auxiliary opened by us should NOT be blocked", + script: "openedWindow.location.replace('file_our_auxiliary_navigation_by_location.html')", + shouldBeBlocked: false, + }, + { + desc: "Test 2: location.assign on auxiliary opened by us should be blocked without allow-same-origin", + script: "openedWindow.location.assign('file_our_auxiliary_navigation_by_location.html')", + shouldBeBlocked: true, + }, + { + desc: "Test 3: location.href on auxiliary opened by us should NOT be blocked", + script: "openedWindow.location.href = 'file_our_auxiliary_navigation_by_location.html'", + shouldBeBlocked: false, + }, + { + desc: "Test 4: location.hash on auxiliary opened by us should be blocked without allow-same-origin", + script: "openedWindow.location.hash = 'wibble'", + shouldBeBlocked: true, + }, + ]; + + function runNextTest() { + ++testCaseIndex; + if (testCaseIndex == testCases.length) { + SpecialPowers.wrap(window.testIframe).eval("openedWindow.close()"); + SimpleTest.finish(); + return; + } + + runScriptNavigationTest(testCases[testCaseIndex]); + } + + window.onmessage = runNextTest; + + window.onload = function() { + SpecialPowers.wrap(window.testIframe).eval("var openedWindow = window.open('file_our_auxiliary_navigation_by_location.html', 'ourWindow')"); + }; +</script> + +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a> +<p id="display"></p> +<div id="content"> +Tests for Bug 785310 +</div> + +<iframe name="testIframe" sandbox="allow-scripts allow-popups"></iframe> +</body> +</html> diff --git a/docshell/test/iframesandbox/test_parent_navigation_by_location.html b/docshell/test/iframesandbox/test_parent_navigation_by_location.html new file mode 100644 index 0000000000..ac6977a3f3 --- /dev/null +++ b/docshell/test/iframesandbox/test_parent_navigation_by_location.html @@ -0,0 +1,75 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=785310 +html5 sandboxed iframe should not be able to perform top navigation with scripts allowed +--> +<head> +<meta charset="utf-8"> +<title>Test for Bug 785310 - iframe sandbox parent navigation by location tests</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + +<script> + SimpleTest.waitForExplicitFinish(); + + function runScriptNavigationTest(testCase) { + window.onmessage = function(event) { + if (event.data != "parentIframe") { + ok(false, "event.data: got '" + event.data + "', expected 'parentIframe'"); + } + ok(false, testCase.desc + " - parent navigation was NOT blocked"); + runNextTest(); + }; + try { + window.parentIframe.childIframe.eval(testCase.script); + } catch (e) { + ok(true, testCase.desc + " - " + e.message); + runNextTest(); + } + } + + var testCaseIndex = -1; + var testCases = [ + { + desc: "Test 1: parent.location.replace should be blocked even when sandboxed with allow-same-origin allow-top-navigation", + script: "parent.location.replace('file_parent_navigation_by_location.html')", + }, + { + desc: "Test 2: parent.location.assign should be blocked even when sandboxed with allow-same-origin allow-top-navigation", + script: "parent.location.assign('file_parent_navigation_by_location.html')", + }, + { + desc: "Test 3: parent.location.href should be blocked even when sandboxed with allow-same-origin allow-top-navigation", + script: "parent.location.href = 'file_parent_navigation_by_location.html'", + }, + { + desc: "Test 4: parent.location.hash should be blocked even when sandboxed with allow-same-origin allow-top-navigation", + script: "parent.location.hash = 'wibble'", + }, + ]; + + function runNextTest() { + ++testCaseIndex; + if (testCaseIndex == testCases.length) { + SimpleTest.finish(); + return; + } + + runScriptNavigationTest(testCases[testCaseIndex]); + } + + window.onmessage = runNextTest; +</script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a> +<p id="display"></p> +<div id="content"> +Tests for Bug 785310 +</div> + +<iframe name="parentIframe" src="file_parent_navigation_by_location.html"></iframe> + +</body> +</html> diff --git a/docshell/test/iframesandbox/test_sibling_navigation_by_location.html b/docshell/test/iframesandbox/test_sibling_navigation_by_location.html new file mode 100644 index 0000000000..d7508d5748 --- /dev/null +++ b/docshell/test/iframesandbox/test_sibling_navigation_by_location.html @@ -0,0 +1,78 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=785310 +html5 sandboxed iframe should not be able to perform top navigation with scripts allowed +--> +<head> +<meta charset="utf-8"> +<title>Test for Bug 785310 - iframe sandbox sibling navigation by location tests</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + +<script> + SimpleTest.waitForExplicitFinish(); + + function runScriptNavigationTest(testCase) { + window.onmessage = function(event) { + if (event.data != "siblingIframe") { + ok(false, "event.data: got '" + event.data + "', expected 'siblingIframe'"); + } + + ok(false, testCase.desc + " - sibling navigation was NOT blocked"); + runNextTest(); + }; + + try { + window.testIframe.eval(testCase.script); + } catch (e) { + ok(true, testCase.desc + " - " + e.message); + runNextTest(); + } + } + + var testCaseIndex = -1; + var testCases = [ + { + desc: "Test 1: sibling location.replace should be blocked even when sandboxed with allow-same-origin allow-top-navigation", + script: "parent['siblingIframe'].location.replace('file_sibling_navigation_by_location.html')", + }, + { + desc: "Test 2: sibling location.assign should be blocked even when sandboxed with allow-same-origin allow-top-navigation", + script: "parent['siblingIframe'].location.assign('file_sibling_navigation_by_location.html')", + }, + { + desc: "Test 3: sibling location.href should be blocked even when sandboxed with allow-same-origin allow-top-navigation", + script: "parent['siblingIframe'].location.href = 'file_sibling_navigation_by_location.html'", + }, + { + desc: "Test 4: sibling location.hash should be blocked even when sandboxed with allow-same-origin allow-top-navigation", + script: "parent['siblingIframe'].location.hash = 'wibble'", + }, + ]; + + function runNextTest() { + ++testCaseIndex; + if (testCaseIndex == testCases.length) { + SimpleTest.finish(); + return; + } + + runScriptNavigationTest(testCases[testCaseIndex]); + } + + window.onmessage = runNextTest; +</script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a> +<p id="display"></p> +<div id="content"> +Tests for Bug 785310 +</div> + +<iframe name="testIframe" sandbox="allow-scripts allow-same-origin allow-top-navigation"></iframe> +<iframe name="siblingIframe" src="file_sibling_navigation_by_location.html"></iframe> + +</body> +</html> diff --git a/docshell/test/iframesandbox/test_top_navigation_by_location.html b/docshell/test/iframesandbox/test_top_navigation_by_location.html new file mode 100644 index 0000000000..248f854bbf --- /dev/null +++ b/docshell/test/iframesandbox/test_top_navigation_by_location.html @@ -0,0 +1,167 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=785310 +html5 sandboxed iframe should not be able to perform top navigation with scripts allowed +--> +<head> +<meta charset="utf-8"> +<title>Test for Bug 785310 - iframe sandbox top navigation by location tests</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +<script> + SimpleTest.waitForExplicitFinish(); + + var testWin; + + function runScriptNavigationTest(testCase) { + window.onmessage = function(event) { + if (event.data != "newTop") { + ok(false, "event.data: got '" + event.data + "', expected 'newTop'"); + } + ok(!testCase.shouldBeBlocked, testCase.desc + " - top navigation was NOT blocked"); + runNextTest(); + }; + try { + SpecialPowers.wrap(testWin[testCase.iframeName]).eval(testCase.script); + } catch (e) { + ok(testCase.shouldBeBlocked, testCase.desc + " - " + SpecialPowers.wrap(e).message); + runNextTest(); + } + } + + var testCaseIndex = -1; + var testCases = [ + { + desc: "Test 1: top.location.replace should be blocked when sandboxed without allow-top-navigation", + script: "top.location.replace('file_top_navigation_by_location.html')", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 2: top.location.assign should be blocked when sandboxed without allow-top-navigation", + script: "top.location.assign('file_top_navigation_by_location.html')", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 3: top.location.href should be blocked when sandboxed without allow-top-navigation", + script: "top.location.href = 'file_top_navigation_by_location.html'", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 4: top.location.pathname should be blocked when sandboxed without allow-top-navigation", + script: "top.location.pathname = top.location.pathname", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 5: top.location should be blocked when sandboxed without allow-top-navigation", + script: "top.location = 'file_top_navigation_by_location.html'", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 6: top.location.hash should be blocked when sandboxed without allow-top-navigation", + script: "top.location.hash = 'wibble'", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 7: top.location.replace should NOT be blocked when sandboxed with allow-same-origin allow-top-navigation", + script: "top.location.replace('file_top_navigation_by_location.html')", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 8: top.location.assign should NOT be blocked when sandboxed with allow-same-origin allow-top-navigation", + script: "top.location.assign('file_top_navigation_by_location.html')", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 9: top.location.href should NOT be blocked when sandboxed with allow-same-origin allow-top-navigation", + script: "top.location.href = 'file_top_navigation_by_location.html'", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 10: top.location.pathname should NOT be blocked when sandboxed with allow-same-origin allow-top-navigation", + script: "top.location.pathname = top.location.pathname", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 11: top.location should NOT be blocked when sandboxed with allow-same-origin allow-top-navigation", + script: "top.location = 'file_top_navigation_by_location.html'", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 12: top.location.hash should NOT be blocked when sandboxed with allow-same-origin allow-top-navigation", + script: "top.location.hash = 'wibble'", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 13: top.location.replace should NOT be blocked when sandboxed with allow-top-navigation, but without allow-same-origin", + script: "top.location.replace('file_top_navigation_by_location.html')", + iframeName: "if3", + shouldBeBlocked: false, + }, + { + desc: "Test 14: top.location.assign should be blocked when sandboxed with allow-top-navigation, but without allow-same-origin", + script: "top.location.assign('file_top_navigation_by_location.html')", + iframeName: "if3", + shouldBeBlocked: true, + }, + { + desc: "Test 15: top.location.href should NOT be blocked when sandboxed with allow-top-navigation, but without allow-same-origin", + script: "top.location.href = 'file_top_navigation_by_location.html'", + iframeName: "if3", + shouldBeBlocked: false, + }, + { + desc: "Test 16: top.location.pathname should be blocked when sandboxed with allow-top-navigation, but without allow-same-origin", + script: "top.location.pathname = top.location.pathname", + iframeName: "if3", + shouldBeBlocked: true, + }, + { + desc: "Test 17: top.location should NOT be blocked when sandboxed with allow-top-navigation, but without allow-same-origin", + script: "top.location = 'file_top_navigation_by_location.html'", + iframeName: "if3", + shouldBeBlocked: false, + }, + { + desc: "Test 18: top.location.hash should be blocked when sandboxed with allow-top-navigation, but without allow-same-origin", + script: "top.location.hash = 'wibble'", + iframeName: "if3", + shouldBeBlocked: true, + }, + ]; + + function runNextTest() { + ++testCaseIndex; + if (testCaseIndex == testCases.length) { + testWin.close(); + SimpleTest.finish(); + return; + } + + runScriptNavigationTest(testCases[testCaseIndex]); + } + + window.onmessage = runNextTest; + testWin = window.open("file_top_navigation_by_location.html", "newTop"); +</script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a> +<p id="display"></p> +<div id="content"> +Tests for Bug 785310 +</div> +</body> +</html> diff --git a/docshell/test/iframesandbox/test_top_navigation_by_location_exotic.html b/docshell/test/iframesandbox/test_top_navigation_by_location_exotic.html new file mode 100644 index 0000000000..11b7c78699 --- /dev/null +++ b/docshell/test/iframesandbox/test_top_navigation_by_location_exotic.html @@ -0,0 +1,204 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=785310 +html5 sandboxed iframe should not be able to perform top navigation with scripts allowed +--> +<head> +<meta charset="utf-8"> +<title>Test for Bug 785310 - iframe sandbox top navigation by location via exotic means tests</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +<script> + SimpleTest.waitForExplicitFinish(); + + var testWin; + + function runScriptNavigationTest(testCase) { + window.onmessage = function(event) { + if (event.data.name != "newWindow") { + ok(false, "event.data.name: got '" + event.data.name + "', expected 'newWindow'"); + } + var diag = "top navigation was " + (event.data.blocked ? "" : "NOT ") + "blocked"; + ok((testCase.shouldBeBlocked == event.data.blocked), testCase.desc + " - " + diag); + runNextTest(); + }; + try { + testWin[testCase.iframeName].eval(testCase.script); + } catch (e) { + ok(testCase.shouldBeBlocked, testCase.desc + " - " + e.message); + runNextTest(); + } + } + + var testCaseIndex = -1; + var testCases = [ + { + desc: "Test 1: location.replace.call(top.location, ...) should be blocked when sandboxed without allow-top-navigation", + script: "location.replace.call(top.location, 'file_top_navigation_by_location_exotic.html')", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 2: location.replace.bind(top.location, ...) should be blocked when sandboxed without allow-top-navigation", + script: "location.replace.bind(top.location, 'file_top_navigation_by_location_exotic.html')()", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 3: Function.bind.call(location.replace, top.location, ...) should be blocked when sandboxed without allow-top-navigation", + script: "Function.bind.call(location.replace, top.location, 'file_top_navigation_by_location_exotic.html')()", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 4: location.replace.call(top.location, ...) should NOT be blocked when sandboxed with allow-top-navigation", + script: "location.replace.call(top.location, 'file_top_navigation_by_location_exotic.html')", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 5: location.replace.bind(top.location, ...) should NOT be blocked when sandboxed with allow-top-navigation", + script: "location.replace.bind(top.location, 'file_top_navigation_by_location_exotic.html')()", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 6: Function.bind.call(location.replace, top.location, ...) should NOT be blocked when sandboxed with allow-top-navigation", + script: "Function.bind.call(location.replace, top.location, 'file_top_navigation_by_location_exotic.html')()", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 7: top.location.href, via setTimeout, should be blocked when sandboxed without allow-top-navigation", + script: "setTimeout(function() { try { top.location.href = 'file_top_navigation_by_location_exotic.html' } catch (e) { top.onBlock() } }, 0)", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 8: top.location.href, via setTimeout, should NOT be blocked when sandboxed with allow-top-navigation", + script: "setTimeout(function() { try { top.location.href = 'file_top_navigation_by_location_exotic.html' } catch(e) { top.onBlock() } }, 0)", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 9: top.location.href, via eval, should be blocked when sandboxed without allow-top-navigation", + script: "eval('top.location.href = \"file_top_navigation_by_location_exotic.html\"')", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 10: top.location.href, via eval, should NOT be blocked when sandboxed with allow-top-navigation", + script: "eval('top.location.href = \"file_top_navigation_by_location_exotic.html\"')", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 11: top.location.href, via anonymous function, should be blocked when sandboxed without allow-top-navigation", + script: "(function() { top.location.href = 'file_top_navigation_by_location_exotic.html' })()", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 12: top.location.href, via anonymous function, should NOT be blocked when sandboxed with allow-top-navigation", + script: "(function() { top.location.href = 'file_top_navigation_by_location_exotic.html' })()", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 13: top.location.href, via function inserted in top, should be blocked when sandboxed without allow-top-navigation", + script: "top.doTest = function() { top.location.href = 'file_top_navigation_by_location_exotic.html' }; top.doTest();", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 14: top.location.href, via function inserted in top, should NOT be blocked when sandboxed with allow-top-navigation", + script: "top.doTest = function() { top.location.href = 'file_top_navigation_by_location_exotic.html' }; top.doTest();", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 15: top.location.href, via function inserted in us by top, should NOT be blocked when sandboxed without allow-top-navigation", + script: "top.eval('window[\"if1\"].doTest = function() { top.location.href = \"file_top_navigation_by_location_exotic.html\" };'), doTest();", + iframeName: "if1", + shouldBeBlocked: false, + }, + { + desc: "Test 16: top.location.href, via function inserted in top, should NOT be blocked when sandboxed with allow-top-navigation", + script: "top.eval('window[\"if2\"].doTest = function() { top.location.href = \"file_top_navigation_by_location_exotic.html\" };'), doTest();", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 17: top.location.href, via function in top, should NOT be blocked when sandboxed without allow-top-navigation", + script: "top.setOwnHref()", + iframeName: "if1", + shouldBeBlocked: false, + }, + { + desc: "Test 18: top.location.href, via function in top, should NOT be blocked when sandboxed with allow-top-navigation", + script: "top.setOwnHref()", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 19: top.location.href, via eval in top, should NOT be blocked when sandboxed without allow-top-navigation", + script: "top.eval('location.href = \"file_top_navigation_by_location_exotic.html\"')", + iframeName: "if1", + shouldBeBlocked: false, + }, + { + desc: "Test 20: top.location.href, via eval in top, should NOT be blocked when sandboxed with allow-top-navigation", + script: "top.eval('location.href = \"file_top_navigation_by_location_exotic.html\"')", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 21: top.location.href, via eval in top calling us, should be blocked when sandboxed without allow-top-navigation", + script: "function doTest() { top.location.href = 'file_top_navigation_by_location_exotic.html' } top.eval('window[\"if1\"].doTest()');", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 22: top.location.href, via eval in top calling us, should NOT be blocked when sandboxed with allow-top-navigation", + script: "function doTest() { top.location.href = 'file_top_navigation_by_location_exotic.html' } top.eval('window[\"if2\"].doTest()');", + iframeName: "if2", + shouldBeBlocked: false, + }, + { + desc: "Test 23: top.location.href, via function bound to top, should be blocked when sandboxed without allow-top-navigation", + script: "(function() { top.location.href = 'file_top_navigation_by_location_exotic.html' }).bind(top)();", + iframeName: "if1", + shouldBeBlocked: true, + }, + { + desc: "Test 24: top.location.href, via function bound to top, should NOT be blocked when sandboxed with allow-top-navigation", + script: "(function() { top.location.href = 'file_top_navigation_by_location_exotic.html' }).bind(top)();", + iframeName: "if2", + shouldBeBlocked: false, + }, + ]; + + function runNextTest() { + ++testCaseIndex; + if (testCaseIndex == testCases.length) { + testWin.close(); + SimpleTest.finish(); + return; + } + + runScriptNavigationTest(testCases[testCaseIndex]); + } + + window.onmessage = runNextTest; + testWin = window.open("file_top_navigation_by_location_exotic.html", "newWindow"); +</script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a> +<p id="display"></p> +<div id="content"> +Tests for Bug 785310 +</div> +</body> +</html> diff --git a/docshell/test/iframesandbox/test_top_navigation_by_user_activation.html b/docshell/test/iframesandbox/test_top_navigation_by_user_activation.html new file mode 100644 index 0000000000..b462c54d7b --- /dev/null +++ b/docshell/test/iframesandbox/test_top_navigation_by_user_activation.html @@ -0,0 +1,74 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1744321 +--> +<head> +<meta charset="utf-8"> +<title>Iframe sandbox top navigation by user activation</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +<script> +function waitForMessage(aCallback) { + return new Promise((aResolve) => { + window.addEventListener("message", function listener(aEvent) { + aCallback(aEvent); + aResolve(); + }, { once: true }); + }); +} + +[ + { + desc: "A same-origin iframe in sandbox with 'allow-top-navigation-by-user-activation' cannot navigate its top level page, if the navigation is not triggered by a user gesture", + sameOrigin: true, + userGesture: false, + }, + { + desc: "A same-origin iframe in sandbox with 'allow-top-navigation-by-user-activation' can navigate its top level page, if the navigation is triggered by a user gesture", + sameOrigin: true, + userGesture: true, + }, + { + desc: "A cross-origin iframe in sandbox with 'allow-top-navigation-by-user-activation' cannot navigate its top level page, if the navigation is not triggered by a user gesture", + sameOrigin: false, + userGesture: false, + }, + { + desc: "A cross-origin iframe in sandbox with 'allow-top-navigation-by-user-activation' can navigate its top level page, if the navigation is triggered by a user gesture", + sameOrigin: false, + userGesture: true, + }, +].forEach(({desc, sameOrigin, userGesture}) => { + add_task(async function() { + info(`Test: ${desc}`); + + let url = "file_top_navigation_by_user_activation.html"; + if (sameOrigin) { + url = `${location.origin}/tests/docshell/test/iframesandbox/${url}`; + } + + let promise = waitForMessage((e) => { + is(e.data, "READY", "Ready for test"); + }); + let testWin = window.open(url); + await promise; + + promise = waitForMessage((e) => { + is(e.data, userGesture ? "NAVIGATED" : "BLOCKED", "Check the result"); + }); + testWin.postMessage(userGesture ? "CLICK" : "SCRIPT", "*"); + await promise; + testWin.close(); + }); +}); +</script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1744321">Mozilla Bug 1744321</a> +<p id="display"></p> +<div id="content"> +Tests for Bug 1744321 +</div> +</body> +</html> diff --git a/docshell/test/mochitest/bug1422334_redirect.html b/docshell/test/mochitest/bug1422334_redirect.html new file mode 100644 index 0000000000..eec7fda2c7 --- /dev/null +++ b/docshell/test/mochitest/bug1422334_redirect.html @@ -0,0 +1,3 @@ +<html> + <body>You should never see this</body> +</html> diff --git a/docshell/test/mochitest/bug1422334_redirect.html^headers^ b/docshell/test/mochitest/bug1422334_redirect.html^headers^ new file mode 100644 index 0000000000..fbf2d1b745 --- /dev/null +++ b/docshell/test/mochitest/bug1422334_redirect.html^headers^ @@ -0,0 +1,2 @@ +HTTP 302 Moved Temporarily +Location: ../navigation/blank.html?x=y diff --git a/docshell/test/mochitest/bug404548-subframe.html b/docshell/test/mochitest/bug404548-subframe.html new file mode 100644 index 0000000000..9a248b40b3 --- /dev/null +++ b/docshell/test/mochitest/bug404548-subframe.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<html> +<body onload="setTimeout(function() { window.location = 'bug404548-subframe_window.html'; }, 10)"> +<iframe srcdoc="<body onpagehide='var p = window.parent.opener; var e = window.frameElement; e.parentNode.removeChild(e); if (e.parentNode == null && e.contentWindow == null) { p.firstRemoved = true; }'>"> +</iframe> +<iframe srcdoc="<body onpagehide='window.parent.opener.secondHidden = true;'>"> +</iframe> diff --git a/docshell/test/mochitest/bug404548-subframe_window.html b/docshell/test/mochitest/bug404548-subframe_window.html new file mode 100644 index 0000000000..82ea73ea83 --- /dev/null +++ b/docshell/test/mochitest/bug404548-subframe_window.html @@ -0,0 +1 @@ +<body onload='window.opener.finishTest()'> diff --git a/docshell/test/mochitest/bug413310-post.sjs b/docshell/test/mochitest/bug413310-post.sjs new file mode 100644 index 0000000000..f87937ab56 --- /dev/null +++ b/docshell/test/mochitest/bug413310-post.sjs @@ -0,0 +1,10 @@ +function handleRequest(request, response) { + response.setHeader("Content-Type", "text/html"); + response.write( + "<body onload='window.parent.onloadCount++'>" + + request.method + + " " + + Date.now() + + "</body>" + ); +} diff --git a/docshell/test/mochitest/bug413310-subframe.html b/docshell/test/mochitest/bug413310-subframe.html new file mode 100644 index 0000000000..bcff1886fd --- /dev/null +++ b/docshell/test/mochitest/bug413310-subframe.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<html> + <body onload="window.parent.onloadCount++"> + <form action="bug413310-post.sjs" method="POST"> + </form> + </body> +</html> diff --git a/docshell/test/mochitest/bug529119-window.html b/docshell/test/mochitest/bug529119-window.html new file mode 100644 index 0000000000..f1908835a7 --- /dev/null +++ b/docshell/test/mochitest/bug529119-window.html @@ -0,0 +1,7 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test bug 529119, sub-window</title> +<body onload="window.opener.windowLoaded();"> +</body> +</html> diff --git a/docshell/test/mochitest/bug530396-noref.sjs b/docshell/test/mochitest/bug530396-noref.sjs new file mode 100644 index 0000000000..6a65882160 --- /dev/null +++ b/docshell/test/mochitest/bug530396-noref.sjs @@ -0,0 +1,22 @@ +function handleRequest(request, response) { + response.setHeader("Content-Type", "text/html"); + response.setHeader("Cache-Control", "no-cache"); + response.write("<body onload='"); + + if (!request.hasHeader("Referer")) { + response.write("window.parent.onloadCount++;"); + } + + if (request.queryString == "newwindow") { + response.write( + "if (window.opener) { window.opener.parent.onloadCount++; window.opener.parent.doNextStep(); }" + ); + response.write("if (!window.opener) window.close();"); + response.write("'>"); + } else { + response.write("window.parent.doNextStep();'>"); + } + + response.write(request.method + " " + Date.now()); + response.write("</body>"); +} diff --git a/docshell/test/mochitest/bug530396-subframe.html b/docshell/test/mochitest/bug530396-subframe.html new file mode 100644 index 0000000000..be81b9f144 --- /dev/null +++ b/docshell/test/mochitest/bug530396-subframe.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<html> + <body onload="window.parent.onloadCount++"> + <a href="bug530396-noref.sjs" rel="noreferrer foo" id="target1">bug530396-noref.sjs</a> + <a href="bug530396-noref.sjs?newwindow" rel="nofollow noreferrer" id="target2" target="newwindow">bug530396-noref.sjs with new window</a> + </body> +</html> diff --git a/docshell/test/mochitest/bug570341_recordevents.html b/docshell/test/mochitest/bug570341_recordevents.html new file mode 100644 index 0000000000..45b04866ec --- /dev/null +++ b/docshell/test/mochitest/bug570341_recordevents.html @@ -0,0 +1,21 @@ +<html> +<head> +<script> + var start = Date.now(); + window._testing_js_start = Date.now(); + window["_testing_js_after_" + document.readyState] = start; + document.addEventListener("DOMContentLoaded", + function() { + window._testing_evt_DOMContentLoaded = Date.now(); + }, true); + document.addEventListener("readystatechange", function() { + window["_testing_evt_DOM_" + document.readyState] = Date.now(); + }, true); + function recordLoad() { + window._testing_evt_load = Date.now(); + } +</script> +</head> +<body onload="recordLoad()">This document collects time +for events related to the page load progress.</body> +</html> diff --git a/docshell/test/mochitest/bug668513_redirect.html b/docshell/test/mochitest/bug668513_redirect.html new file mode 100644 index 0000000000..1b8f66c631 --- /dev/null +++ b/docshell/test/mochitest/bug668513_redirect.html @@ -0,0 +1 @@ +<html><body>This document is redirected to a blank document.</body></html> diff --git a/docshell/test/mochitest/bug668513_redirect.html^headers^ b/docshell/test/mochitest/bug668513_redirect.html^headers^ new file mode 100644 index 0000000000..0e785833c6 --- /dev/null +++ b/docshell/test/mochitest/bug668513_redirect.html^headers^ @@ -0,0 +1,2 @@ +HTTP 302 Moved Temporarily +Location: navigation/blank.html diff --git a/docshell/test/mochitest/bug691547_frame.html b/docshell/test/mochitest/bug691547_frame.html new file mode 100644 index 0000000000..00172f7119 --- /dev/null +++ b/docshell/test/mochitest/bug691547_frame.html @@ -0,0 +1,12 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=691547 +--> +<head> + <title>Test for Bug 691547</title> +</head> +<body> +<iframe style="width:95%"></iframe> +</body> +</html> diff --git a/docshell/test/mochitest/clicker.html b/docshell/test/mochitest/clicker.html new file mode 100644 index 0000000000..b655e27ea5 --- /dev/null +++ b/docshell/test/mochitest/clicker.html @@ -0,0 +1,7 @@ +<!doctype html> +<script> + "use strict"; + let target = window.opener ? window.opener : window.parent; + + onmessage = ({data}) => target.postMessage({}, "*"); +</script> diff --git a/docshell/test/mochitest/double_submit.sjs b/docshell/test/mochitest/double_submit.sjs new file mode 100644 index 0000000000..487150d456 --- /dev/null +++ b/docshell/test/mochitest/double_submit.sjs @@ -0,0 +1,79 @@ +"use strict"; + +let self = this; + +let { setTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +const CC = Components.Constructor; +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); + +function log(str) { + // dump(`LOG: ${str}\n`); +} + +function readStream(inputStream) { + let available = 0; + let result = []; + while ((available = inputStream.available()) > 0) { + result.push(inputStream.readBytes(available)); + } + + return result.join(""); +} + +function now() { + return Date.now(); +} + +async function handleRequest(request, response) { + log("Get query parameters"); + Cu.importGlobalProperties(["URLSearchParams"]); + let params = new URLSearchParams(request.queryString); + + let start = now(); + let delay = parseInt(params.get("delay")) || 0; + log(`Delay for ${delay}`); + + let message = "good"; + if (request.method !== "POST") { + message = "bad"; + } else { + log("Read POST body"); + let body = new URLSearchParams( + readStream(new BinaryInputStream(request.bodyInputStream)) + ); + message = body.get("token") || "bad"; + log(`The result was ${message}`); + } + + let body = `<!doctype html> + <script> + "use strict"; + let target = (opener || parent); + target.postMessage(${JSON.stringify(message)}, '*'); + </script>`; + + // Sieze power from the response to allow manually transmitting data at any + // rate we want, so we can delay transmitting headers. + response.seizePower(); + + log(`Writing HTTP status line at ${now() - start}`); + response.write("HTTP/1.1 200 OK\r\n"); + + await new Promise(resolve => setTimeout(() => resolve(), delay)); + + log(`Delay completed at ${now() - start}`); + response.write("Content-Type: text/html\r\n"); + response.write(`Content-Length: ${body.length}\r\n`); + response.write("\r\n"); + response.write(body); + response.finish(); + + log("Finished"); +} diff --git a/docshell/test/mochitest/dummy_page.html b/docshell/test/mochitest/dummy_page.html new file mode 100644 index 0000000000..59bf2a5f8f --- /dev/null +++ b/docshell/test/mochitest/dummy_page.html @@ -0,0 +1,6 @@ +<html> +<head> <meta charset="utf-8"> </head> + <body> + just a dummy html file + </body> +</html> diff --git a/docshell/test/mochitest/file_anchor_scroll_after_document_open.html b/docshell/test/mochitest/file_anchor_scroll_after_document_open.html new file mode 100644 index 0000000000..7903380eac --- /dev/null +++ b/docshell/test/mochitest/file_anchor_scroll_after_document_open.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<script> + if (location.hash == "#target") { + parent.postMessage("haveHash", "*"); + } else { + document.addEventListener("DOMContentLoaded", function() { + document.open(); + document.write("<!DOCTYPE html><html style='height: 100%'><body style='height: 100%'><div style='height: 200%'></div><div id='target'></div></body></html>"); + document.close(); + // Notify parent via postMessage, since otherwise exceptions will not get + // caught by its onerror handler. + parent.postMessage("doTest", "*"); + }); + } +</script> diff --git a/docshell/test/mochitest/file_bfcache_plus_hash_1.html b/docshell/test/mochitest/file_bfcache_plus_hash_1.html new file mode 100644 index 0000000000..199f6003e0 --- /dev/null +++ b/docshell/test/mochitest/file_bfcache_plus_hash_1.html @@ -0,0 +1,24 @@ +<html><body> + Popup 1 + <script type="application/javascript"> + var bc = new BroadcastChannel("bug646641_1"); + window.onload = () => { + bc.postMessage({ message: "childLoad", num: 1 }) + } + + window.onpageshow = () => { + bc.postMessage({ message: "childPageshow", num: 1 }) + } + bc.onmessage = (msgEvent) => { + var msg = msgEvent.data; + if (msg == "pushState") { + history.pushState("", "", ""); + location = "file_bfcache_plus_hash_2.html"; + } else if (msg == "close") { + bc.postMessage({ message: "closed" }); + bc.close(); + window.close(); + } + } + </script> +</body></html> diff --git a/docshell/test/mochitest/file_bfcache_plus_hash_2.html b/docshell/test/mochitest/file_bfcache_plus_hash_2.html new file mode 100644 index 0000000000..c27d4eaa3b --- /dev/null +++ b/docshell/test/mochitest/file_bfcache_plus_hash_2.html @@ -0,0 +1,17 @@ +<html><body> + Popup 2 + <script type="application/javascript"> + var bc = new BroadcastChannel("bug646641_2"); + window.onload = () => { + bc.postMessage({ message: "childLoad", num: 2 }) + requestAnimationFrame(() => bc.postMessage({message: "childPageshow", num: 2})); + } + bc.onmessage = (msgEvent) => { + var msg = msgEvent.data; + if (msg == "go-2") { + history.go(-2); + bc.close(); + } + } + </script> +</body></html> diff --git a/docshell/test/mochitest/file_bug1121701_1.html b/docshell/test/mochitest/file_bug1121701_1.html new file mode 100644 index 0000000000..62c58495f7 --- /dev/null +++ b/docshell/test/mochitest/file_bug1121701_1.html @@ -0,0 +1,29 @@ +<script> + var bc = new BroadcastChannel("file_bug1121701_1"); + var pageHideAsserts = undefined; + bc.onmessage = (msgEvent) => { + var msg = msgEvent.data; + var command = msg.command; + if (command == "setInnerHTML") { + document.body.innerHTML = "modified"; + window.onpagehide = function(event) { + window.onpagehide = null; + pageHideAsserts = {}; + pageHideAsserts.persisted = event.persisted; + // eslint-disable-next-line no-unsanitized/property + pageHideAsserts.innerHTML = window.document.body.innerHTML; + }; + window.location.href = msg.testUrl2; + } else if (command == "close") { + bc.postMessage({command: "closed"}); + bc.close(); + window.close(); + } + } + window.onpageshow = function(e) { + var msg = {command: "child1PageShow", persisted: e.persisted, pageHideAsserts}; + // eslint-disable-next-line no-unsanitized/property + msg.innerHTML = window.document.body.innerHTML; + bc.postMessage(msg); + }; +</script> diff --git a/docshell/test/mochitest/file_bug1121701_2.html b/docshell/test/mochitest/file_bug1121701_2.html new file mode 100644 index 0000000000..6cec88cd5d --- /dev/null +++ b/docshell/test/mochitest/file_bug1121701_2.html @@ -0,0 +1,23 @@ +<script> + var bc = new BroadcastChannel("file_bug1121701_2"); + bc.onmessage = (msgEvent) => { + var msg = msgEvent.data; + var command = msg.command; + if (command == "setInnerHTML") { + window.document.body.innerHTML = "<img>"; + window.onmessage = function() { + bc.postMessage({command: "onmessage"}); + window.document.body.firstChild.src = msg.location; + bc.close(); + }; + window.onbeforeunload = function() { + window.postMessage("foo", "*"); + }; + + history.back(); + } + } + window.onpageshow = function(e) { + bc.postMessage({command: "child2PageShow", persisted: e.persisted}); + }; +</script> diff --git a/docshell/test/mochitest/file_bug1151421.html b/docshell/test/mochitest/file_bug1151421.html new file mode 100644 index 0000000000..7bb8c8f363 --- /dev/null +++ b/docshell/test/mochitest/file_bug1151421.html @@ -0,0 +1,19 @@ +<html> +<head> +<style> +body, html { + height: 100%; +} +.spacer { + height: 80%; +} +</style> +</head> +<body onload='(parent || opener).childLoad()'> + +<div class="spacer"></div> +<div id="content">content</div> +<div class="spacer"></div> + +</body> +</html> diff --git a/docshell/test/mochitest/file_bug1186774.html b/docshell/test/mochitest/file_bug1186774.html new file mode 100644 index 0000000000..9af95b09bd --- /dev/null +++ b/docshell/test/mochitest/file_bug1186774.html @@ -0,0 +1 @@ +<div style='height: 9000px;'></div> diff --git a/docshell/test/mochitest/file_bug1450164.html b/docshell/test/mochitest/file_bug1450164.html new file mode 100644 index 0000000000..55e32ce93d --- /dev/null +++ b/docshell/test/mochitest/file_bug1450164.html @@ -0,0 +1,16 @@ +<html> + <head> + <script> + function go() { + var a = window.history.state; + window.history.replaceState(a, "", "1"); + var ok = opener.ok; + var SimpleTest = opener.SimpleTest; + ok("Addition of history in unload did not crash browser"); + SimpleTest.finish(); + } + </script> + </head> + <body onunload="go()"> + </body> +</html> diff --git a/docshell/test/mochitest/file_bug1729662.html b/docshell/test/mochitest/file_bug1729662.html new file mode 100644 index 0000000000..f5710e1c49 --- /dev/null +++ b/docshell/test/mochitest/file_bug1729662.html @@ -0,0 +1,8 @@ +<script> +addEventListener("load", () => { + (new BroadcastChannel("bug1729662")).postMessage("load"); + history.pushState(1, null, location.href); + history.back(); + history.forward(); +}); +</script> diff --git a/docshell/test/mochitest/file_bug1740516_1.html b/docshell/test/mochitest/file_bug1740516_1.html new file mode 100644 index 0000000000..ac8ca71d93 --- /dev/null +++ b/docshell/test/mochitest/file_bug1740516_1.html @@ -0,0 +1,29 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <script> + window.addEventListener("pageshow", ({ persisted }) => { + let bc = new BroadcastChannel("bug1740516_1"); + bc.addEventListener("message", ({ data }) => { + bc.close(); + switch (data) { + case "block_bfcache_and_navigate": + window.blockBFCache = new RTCPeerConnection(); + // Fall through + case "navigate": + document.location = "file_bug1740516_2.html"; + break; + case "close": + window.close(); + break; + } + }); + bc.postMessage(persisted); + }); + </script> +</head> +<body> + <iframe src="file_bug1740516_1_inner.html"></iframe> +</body> +</html> diff --git a/docshell/test/mochitest/file_bug1740516_1_inner.html b/docshell/test/mochitest/file_bug1740516_1_inner.html new file mode 100644 index 0000000000..159c6bde5a --- /dev/null +++ b/docshell/test/mochitest/file_bug1740516_1_inner.html @@ -0,0 +1,15 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <script> + window.addEventListener("pageshow", ({ persisted }) => { + let bc = new BroadcastChannel("bug1740516_1_inner"); + bc.postMessage(persisted); + bc.close(); + }); + </script> +</head> +<body> +</body> +</html> diff --git a/docshell/test/mochitest/file_bug1740516_2.html b/docshell/test/mochitest/file_bug1740516_2.html new file mode 100644 index 0000000000..2dc714feef --- /dev/null +++ b/docshell/test/mochitest/file_bug1740516_2.html @@ -0,0 +1,11 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <script> + addEventListener("pageshow", () => { history.back(); }); + </script> +</head> +<body> +</body> +</html> diff --git a/docshell/test/mochitest/file_bug1741132.html b/docshell/test/mochitest/file_bug1741132.html new file mode 100644 index 0000000000..d863b9f015 --- /dev/null +++ b/docshell/test/mochitest/file_bug1741132.html @@ -0,0 +1,29 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <script> + window.addEventListener("pageshow", ({ persisted }) => { + let bc = new BroadcastChannel("bug1741132"); + bc.addEventListener("message", ({ data: { cmd, arg } }) => { + bc.close(); + switch (cmd) { + case "load": + document.location = arg; + break; + case "go": + window.blockBFCache = new RTCPeerConnection(); + history.go(arg); + break; + case "close": + window.close(); + break; + } + }); + bc.postMessage(persisted); + }); + </script> +</head> +<body> +</body> +</html> diff --git a/docshell/test/mochitest/file_bug1742865.sjs b/docshell/test/mochitest/file_bug1742865.sjs new file mode 100644 index 0000000000..950c30ecd2 --- /dev/null +++ b/docshell/test/mochitest/file_bug1742865.sjs @@ -0,0 +1,77 @@ +Cu.importGlobalProperties(["URLSearchParams"]); + +function handleRequest(request, response) { + if (request.queryString == "reset") { + setState("index", "0"); + response.setStatusLine(request.httpVersion, 200, "Ok"); + response.write("Reset"); + return; + } + + let refresh = ""; + let index = Number(getState("index")); + // index == 0 First load, returns first meta refresh + // index == 1 Second load, caused by first meta refresh, returns second meta refresh + // index == 2 Third load, caused by second meta refresh, doesn't return a meta refresh + let query = new URLSearchParams(request.queryString); + if (index < 2) { + refresh = query.get("seconds"); + if (query.get("crossOrigin") == "true") { + const hosts = ["example.org", "example.com"]; + + let url = `${request.scheme}://${hosts[index]}${request.path}?${request.queryString}`; + refresh += `; url=${url}`; + } + refresh = `<meta http-equiv="Refresh" content="${refresh}">`; + } + // We want to scroll for the first load, and check that the meta refreshes keep the same + // scroll position. + let scroll = index == 0 ? `scrollTo(0, ${query.get("scrollTo")});` : ""; + + setState("index", String(index + 1)); + + response.write( + `<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <meta http-equiv="Cache-Control" content="no-cache"> + ${refresh} + <script> + window.addEventListener("pageshow", () => { + ${scroll} + window.top.opener.postMessage({ + commandType: "pageShow", + commandData: { + inputValue: document.getElementById("input").value, + scrollPosition: window.scrollY, + }, + }, "*"); + }); + window.addEventListener("message", ({ data }) => { + if (data == "changeInputValue") { + document.getElementById("input").value = "1234"; + window.top.opener.postMessage({ + commandType: "onChangedInputValue", + commandData: { + historyLength: history.length, + inputValue: document.getElementById("input").value, + }, + }, "*"); + } else if (data == "loadNext") { + location.href += "&loadnext=1"; + } else if (data == "back") { + history.back(); + } + }); + </script> +</head> +<body> +<input type="text" id="input" value="initial"></input> +<div style='height: 9000px;'></div> +<p> +</p> +</body> +</html>` + ); +} diff --git a/docshell/test/mochitest/file_bug1742865_outer.sjs b/docshell/test/mochitest/file_bug1742865_outer.sjs new file mode 100644 index 0000000000..bad8b23f00 --- /dev/null +++ b/docshell/test/mochitest/file_bug1742865_outer.sjs @@ -0,0 +1,25 @@ +Cu.importGlobalProperties(["URLSearchParams"]); + +function handleRequest(request, response) { + response.write( + `<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <script> + window.addEventListener("message", ({ data }) => { + if (data == "loadNext") { + location.href += "&loadnext=1"; + return; + } + // Forward other messages to the frame. + document.getElementById("frame").contentWindow.postMessage(data, "*"); + }); + </script> +</head> +<body> + <iframe src="file_bug1742865.sjs?${request.queryString}" id="frame"></iframe> +</body> +</html>` + ); +} diff --git a/docshell/test/mochitest/file_bug1743353.html b/docshell/test/mochitest/file_bug1743353.html new file mode 100644 index 0000000000..c08f8d143f --- /dev/null +++ b/docshell/test/mochitest/file_bug1743353.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <script> + window.addEventListener("pageshow", () => { + let bc = new BroadcastChannel("bug1743353"); + bc.addEventListener("message", ({ data: cmd }) => { + switch (cmd) { + case "load": + bc.close(); + document.location += "?1"; + break; + case "back": + window.blockBFCache = new RTCPeerConnection(); + window.addEventListener("pagehide", () => { + bc.postMessage("pagehide"); + }); + window.addEventListener("unload", () => { + bc.postMessage("unload"); + bc.close(); + }); + history.back(); + break; + case "close": + bc.close(); + window.close(); + break; + } + }); + bc.postMessage("pageshow"); + }); + </script> +</head> +<body> +</body> +</html> diff --git a/docshell/test/mochitest/file_bug1747033.sjs b/docshell/test/mochitest/file_bug1747033.sjs new file mode 100644 index 0000000000..14401101b2 --- /dev/null +++ b/docshell/test/mochitest/file_bug1747033.sjs @@ -0,0 +1,110 @@ +"use strict"; + +const BOUNDARY = "BOUNDARY"; + +// waitForPageShow should be false if this is for multipart/x-mixed-replace +// and it's not the last part, Gecko doesn't fire pageshow for those parts. +function documentString(waitForPageShow = true) { + return `<html> + <head> + <script> + let bc = new BroadcastChannel("bug1747033"); + bc.addEventListener("message", ({ data: { cmd, arg = undefined } }) => { + switch (cmd) { + case "load": + location.href = arg; + break; + case "replaceState": + history.replaceState({}, "Replaced state", arg); + bc.postMessage({ "historyLength": history.length, "location": location.href }); + break; + case "back": + history.back(); + break; + case "close": + close(); + break; + } + }); + + function reply() { + bc.postMessage({ "historyLength": history.length, "location": location.href }); + } + + ${waitForPageShow ? `addEventListener("pageshow", reply);` : "reply();"} + </script> + </head> + <body></body> +</html> +`; +} + +function boundary(last = false) { + let b = `--${BOUNDARY}`; + if (last) { + b += "--"; + } + return b + "\n"; +} + +function sendMultipart(response, last = false) { + setState("sendMore", ""); + + response.write(`Content-Type: text/html + +${documentString(last)} +`); + response.write(boundary(last)); +} + +function shouldSendMore() { + return new Promise(resolve => { + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback( + () => { + let sendMore = getState("sendMore"); + if (sendMore !== "") { + timer.cancel(); + resolve(sendMore); + } + }, + 100, + Ci.nsITimer.TYPE_REPEATING_SLACK + ); + }); +} + +async function handleRequest(request, response) { + if (request.queryString == "") { + // This is for non-multipart/x-mixed-replace loads. + response.write(documentString()); + return; + } + + if (request.queryString == "sendNextPart") { + setState("sendMore", "next"); + return; + } + + if (request.queryString == "sendLastPart") { + setState("sendMore", "last"); + return; + } + + response.processAsync(); + + response.setHeader( + "Content-Type", + `multipart/x-mixed-replace; boundary=${BOUNDARY}`, + false + ); + response.setStatusLine(request.httpVersion, 200, "OK"); + + response.write(boundary()); + sendMultipart(response); + while ((await shouldSendMore("sendMore")) !== "last") { + sendMultipart(response); + } + sendMultipart(response, true); + response.finish(); +} diff --git a/docshell/test/mochitest/file_bug1773192_1.html b/docshell/test/mochitest/file_bug1773192_1.html new file mode 100644 index 0000000000..42d5b3fced --- /dev/null +++ b/docshell/test/mochitest/file_bug1773192_1.html @@ -0,0 +1,13 @@ +<script> + let bc = new BroadcastChannel("bug1743353"); + bc.addEventListener("message", ({ data }) => { + if (data == "next") { + location = "file_bug1773192_2.html"; + } else if (data == "close") { + window.close(); + } + }); + window.addEventListener("pageshow", () => { + bc.postMessage({ location: location.href, referrer: document.referrer }); + }); +</script> diff --git a/docshell/test/mochitest/file_bug1773192_2.html b/docshell/test/mochitest/file_bug1773192_2.html new file mode 100644 index 0000000000..3b1e5bcb28 --- /dev/null +++ b/docshell/test/mochitest/file_bug1773192_2.html @@ -0,0 +1,13 @@ +<html> + <head> + <meta http-equiv="Cache-Control" content="no-cache, no-store"> + <meta name="referrer" content="same-origin"> + </head> + <body> + <form method="POST" action="file_bug1773192_3.sjs"></form> + <script> + history.replaceState({}, "", document.referrer); + document.forms[0].submit(); + </script> + </body> +</html> diff --git a/docshell/test/mochitest/file_bug1773192_3.sjs b/docshell/test/mochitest/file_bug1773192_3.sjs new file mode 100644 index 0000000000..ce889c7035 --- /dev/null +++ b/docshell/test/mochitest/file_bug1773192_3.sjs @@ -0,0 +1,3 @@ +function handleRequest(request, response) { + response.write("<script>history.back();</script>"); +} diff --git a/docshell/test/mochitest/file_bug385434_1.html b/docshell/test/mochitest/file_bug385434_1.html new file mode 100644 index 0000000000..5c951f1fa6 --- /dev/null +++ b/docshell/test/mochitest/file_bug385434_1.html @@ -0,0 +1,29 @@ +<!-- +Inner frame for test of bug 385434. +https://bugzilla.mozilla.org/show_bug.cgi?id=385434 +--> +<html> +<head> + <script type="application/javascript"> + function hashchange() { + parent.onIframeHashchange(); + } + + function load() { + parent.onIframeLoad(); + } + + function scroll() { + parent.onIframeScroll(); + } + </script> +</head> + +<body onscroll="scroll()" onload="load()" onhashchange="hashchange()"> +<a href="#link1" id="link1">link1</a> +<!-- Our parent loads us in an iframe with height 100px, so this spacer ensures + that switching between #link1 and #link2 causes us to scroll --> +<div style="height:200px;"></div> +<a href="#link2" id="link2">link2</a> +</body> +</html> diff --git a/docshell/test/mochitest/file_bug385434_2.html b/docshell/test/mochitest/file_bug385434_2.html new file mode 100644 index 0000000000..4aa5ef82b8 --- /dev/null +++ b/docshell/test/mochitest/file_bug385434_2.html @@ -0,0 +1,26 @@ +<!-- +Inner frame for test of bug 385434. +https://bugzilla.mozilla.org/show_bug.cgi?id=385434 +--> +<html> +<head> + <script type="application/javascript"> + function hashchange(e) { + // pass the event back to the parent so it can check its properties. + parent.gSampleEvent = e; + + parent.statusMsg("Hashchange in 2."); + parent.onIframeHashchange(); + } + + function load() { + parent.statusMsg("Loading 2."); + parent.onIframeLoad(); + } + </script> +</head> + +<frameset onload="load()" onhashchange="hashchange(event)"> + <frame src="about:blank" /> +</frameset> +</html> diff --git a/docshell/test/mochitest/file_bug385434_3.html b/docshell/test/mochitest/file_bug385434_3.html new file mode 100644 index 0000000000..34dd68ef45 --- /dev/null +++ b/docshell/test/mochitest/file_bug385434_3.html @@ -0,0 +1,22 @@ +<!-- +Inner frame for test of bug 385434. +https://bugzilla.mozilla.org/show_bug.cgi?id=385434 +--> +<html> +<head> + <script type="application/javascript"> + // Notify our parent if we have a hashchange and once we're done loading. + window.addEventListener("hashchange", parent.onIframeHashchange); + + window.addEventListener("DOMContentLoaded", function() { + // This also should trigger a hashchange, becuase the readystate is + // "interactive", not "complete" during DOMContentLoaded. + window.location.hash = "2"; + }); + + </script> +</head> + +<body> +</body> +</html> diff --git a/docshell/test/mochitest/file_bug475636.sjs b/docshell/test/mochitest/file_bug475636.sjs new file mode 100644 index 0000000000..ee2207b0e5 --- /dev/null +++ b/docshell/test/mochitest/file_bug475636.sjs @@ -0,0 +1,98 @@ +let jsURL = + "javascript:" + + escape( + 'window.parent.postMessage("JS uri ran", "*");\ +return \'\ +<script>\ +window.parent.postMessage("Able to access private: " +\ + window.parent.private, "*");\ +</script>\'' + ); +let dataURL = + "data:text/html," + + escape( + '<!DOCTYPE HTML>\ +<script>\ +try {\ + window.parent.postMessage("Able to access private: " +\ + window.parent.private, "*");\ +}\ +catch (e) {\ + window.parent.postMessage("pass", "*");\ +}\ +</script>' + ); + +let tests = [ + // Plain document should work as normal + '<!DOCTYPE HTML>\ +<script>\ +try {\ + window.parent.private;\ + window.parent.postMessage("pass", "*");\ +}\ +catch (e) {\ + window.parent.postMessage("Unble to access private", "*");\ +}\ +</script>', + + // refresh to plain doc + { refresh: "file_bug475636.sjs?1", doc: "<!DOCTYPE HTML>" }, + + // meta-refresh to plain doc + '<!DOCTYPE HTML>\ +<head>\ + <meta http-equiv="refresh" content="0; url=file_bug475636.sjs?1">\ +</head>', + + // refresh to data url + { refresh: dataURL, doc: "<!DOCTYPE HTML>" }, + + // meta-refresh to data url + '<!DOCTYPE HTML>\ +<head>\ + <meta http-equiv="refresh" content="0; url=' + + dataURL + + '">\ +</head>', + + // refresh to js url should not be followed + { + refresh: jsURL, + doc: + '<!DOCTYPE HTML>\ +<script>\ +setTimeout(function() {\ + window.parent.postMessage("pass", "*");\ +}, 2000);\ +</script>', + }, + + // meta refresh to js url should not be followed + '<!DOCTYPE HTML>\ +<head>\ + <meta http-equiv="refresh" content="0; url=' + + jsURL + + '">\ +</head>\ +<script>\ +setTimeout(function() {\ + window.parent.postMessage("pass", "*");\ +}, 2000);\ +</script>', +]; + +function handleRequest(request, response) { + dump("@@@@@@@@@hi there: " + request.queryString + "\n"); + let test = tests[parseInt(request.queryString, 10) - 1]; + response.setHeader("Content-Type", "text/html"); + + if (!test) { + response.write('<script>parent.postMessage("done", "*");</script>'); + } else if (typeof test == "string") { + response.write(test); + } else if (test.refresh) { + response.setHeader("Refresh", "0; url=" + test.refresh); + response.write(test.doc); + } +} diff --git a/docshell/test/mochitest/file_bug509055.html b/docshell/test/mochitest/file_bug509055.html new file mode 100644 index 0000000000..ac30876bbf --- /dev/null +++ b/docshell/test/mochitest/file_bug509055.html @@ -0,0 +1,9 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test inner frame for bug 509055</title> +</head> +<body onhashchange="hashchangeCallback()"> + file_bug509055.html +</body> +</html> diff --git a/docshell/test/mochitest/file_bug511449.html b/docshell/test/mochitest/file_bug511449.html new file mode 100644 index 0000000000..637732dbbf --- /dev/null +++ b/docshell/test/mochitest/file_bug511449.html @@ -0,0 +1,6 @@ +<!DOCTYPE HTML> +<title>Used in test for bug 511449</title> +<input type="text" id="input"> +<script type="text/javascript"> + document.getElementById("input").focus(); +</script> diff --git a/docshell/test/mochitest/file_bug540462.html b/docshell/test/mochitest/file_bug540462.html new file mode 100644 index 0000000000..ab8c07eba5 --- /dev/null +++ b/docshell/test/mochitest/file_bug540462.html @@ -0,0 +1,25 @@ +<html> + <head> + <script> + // <!-- + function test() { + document.open(); + document.write( + `<html> + <body onload='opener.documentWriteLoad(); rel();'> + <a href='foo.html'>foo</a> + <script> + function rel() { setTimeout('location.reload()', 0); } + <\/script> + </body> + </html>` + ); + document.close(); + } + // --> + </script> + </head> + <body onload="setTimeout('test()', 0)"> + Test for bug 540462 + </body> +</html> diff --git a/docshell/test/mochitest/file_bug580069_1.html b/docshell/test/mochitest/file_bug580069_1.html new file mode 100644 index 0000000000..7ab4610334 --- /dev/null +++ b/docshell/test/mochitest/file_bug580069_1.html @@ -0,0 +1,8 @@ +<html> +<body onload='parent.page1Load();'> +file_bug580069_1.html + +<form id='form' action='file_bug580069_2.sjs' method='POST'></form> + +</body> +</html> diff --git a/docshell/test/mochitest/file_bug580069_2.sjs b/docshell/test/mochitest/file_bug580069_2.sjs new file mode 100644 index 0000000000..ff03c74e68 --- /dev/null +++ b/docshell/test/mochitest/file_bug580069_2.sjs @@ -0,0 +1,8 @@ +function handleRequest(request, response) { + response.setHeader("Content-Type", "text/html", false); + response.write( + "<html><body onload='parent.page2Load(\"" + + request.method + + "\")'>file_bug580069_2.sjs</body></html>" + ); +} diff --git a/docshell/test/mochitest/file_bug590573_1.html b/docshell/test/mochitest/file_bug590573_1.html new file mode 100644 index 0000000000..b859ca7469 --- /dev/null +++ b/docshell/test/mochitest/file_bug590573_1.html @@ -0,0 +1,7 @@ +<html> +<body onload='opener.page1Load();' onpageshow='opener.page1PageShow();'> + +<div style='height:10000px' id='div1'>This is a very tall div.</div> + +</body> +</html> diff --git a/docshell/test/mochitest/file_bug590573_2.html b/docshell/test/mochitest/file_bug590573_2.html new file mode 100644 index 0000000000..5f9ca22be4 --- /dev/null +++ b/docshell/test/mochitest/file_bug590573_2.html @@ -0,0 +1,8 @@ +<html> +<body onpopstate='opener.page2Popstate();' onload='opener.page2Load();' + onpageshow='opener.page2PageShow();'> + +<div style='height:300%' id='div2'>The second page also has a big div.</div> + +</body> +</html> diff --git a/docshell/test/mochitest/file_bug598895_1.html b/docshell/test/mochitest/file_bug598895_1.html new file mode 100644 index 0000000000..d21f2b4a5d --- /dev/null +++ b/docshell/test/mochitest/file_bug598895_1.html @@ -0,0 +1 @@ +<script>window.onload = function() { opener.postMessage("loaded", "*"); };</script><body>Should show</body> diff --git a/docshell/test/mochitest/file_bug598895_2.html b/docshell/test/mochitest/file_bug598895_2.html new file mode 100644 index 0000000000..680c9bf22b --- /dev/null +++ b/docshell/test/mochitest/file_bug598895_2.html @@ -0,0 +1 @@ +<script>window.onload = function() { opener.postMessage("loaded", "*"); };</script><body></body> diff --git a/docshell/test/mochitest/file_bug634834.html b/docshell/test/mochitest/file_bug634834.html new file mode 100644 index 0000000000..3ff0897451 --- /dev/null +++ b/docshell/test/mochitest/file_bug634834.html @@ -0,0 +1,5 @@ +<html> +<body> +Nothing to see here; just an empty page. +</body> +</html> diff --git a/docshell/test/mochitest/file_bug637644_1.html b/docshell/test/mochitest/file_bug637644_1.html new file mode 100644 index 0000000000..d21f2b4a5d --- /dev/null +++ b/docshell/test/mochitest/file_bug637644_1.html @@ -0,0 +1 @@ +<script>window.onload = function() { opener.postMessage("loaded", "*"); };</script><body>Should show</body> diff --git a/docshell/test/mochitest/file_bug637644_2.html b/docshell/test/mochitest/file_bug637644_2.html new file mode 100644 index 0000000000..680c9bf22b --- /dev/null +++ b/docshell/test/mochitest/file_bug637644_2.html @@ -0,0 +1 @@ +<script>window.onload = function() { opener.postMessage("loaded", "*"); };</script><body></body> diff --git a/docshell/test/mochitest/file_bug640387.html b/docshell/test/mochitest/file_bug640387.html new file mode 100644 index 0000000000..3a939fb41e --- /dev/null +++ b/docshell/test/mochitest/file_bug640387.html @@ -0,0 +1,26 @@ +<html> +<body onhashchange='hashchange()' onload='load()' onpopstate='popstate()'> + +<script> +function hashchange() { + var f = (opener || parent).childHashchange; + if (f) + f(); +} + +function load() { + var f = (opener || parent).childLoad; + if (f) + f(); +} + +function popstate() { + var f = (opener || parent).childPopstate; + if (f) + f(); +} +</script> + +Not much to see here... +</body> +</html> diff --git a/docshell/test/mochitest/file_bug653741.html b/docshell/test/mochitest/file_bug653741.html new file mode 100644 index 0000000000..3202b52573 --- /dev/null +++ b/docshell/test/mochitest/file_bug653741.html @@ -0,0 +1,13 @@ +<html> +<body onload='(parent || opener).childLoad()'> + +<div style='height:500px; background:yellow'> +<a id='#top'>Top of the page</a> +</div> + +<div id='bottom'> +<a id='#bottom'>Bottom of the page</a> +</div> + +</body> +</html> diff --git a/docshell/test/mochitest/file_bug660404 b/docshell/test/mochitest/file_bug660404 new file mode 100644 index 0000000000..ed773420ef --- /dev/null +++ b/docshell/test/mochitest/file_bug660404 @@ -0,0 +1,13 @@ +--testingtesting +Content-Type: text/html + +<script> + var bc = new BroadcastChannel("bug660404_multipart"); + bc.postMessage({command: "finishTest", + textContent: window.document.documentElement.textContent, + innerHTML: window.document.documentElement.innerHTML + }); + bc.close(); + window.close(); +</script> +--testingtesting-- diff --git a/docshell/test/mochitest/file_bug660404-1.html b/docshell/test/mochitest/file_bug660404-1.html new file mode 100644 index 0000000000..878bd80426 --- /dev/null +++ b/docshell/test/mochitest/file_bug660404-1.html @@ -0,0 +1,12 @@ +<script> + var bc = new BroadcastChannel("bug660404"); + window.onload = function() { + setTimeout(() => { + window.onpagehide = function(ev) { + bc.postMessage({command: "pagehide", persisted: ev.persisted}); + bc.close(); + }; + window.location.href = "file_bug660404"; + }, 0); + }; +</script> diff --git a/docshell/test/mochitest/file_bug660404^headers^ b/docshell/test/mochitest/file_bug660404^headers^ new file mode 100644 index 0000000000..5c821f3f48 --- /dev/null +++ b/docshell/test/mochitest/file_bug660404^headers^ @@ -0,0 +1 @@ +Content-Type: multipart/x-mixed-replace; boundary="testingtesting" diff --git a/docshell/test/mochitest/file_bug662170.html b/docshell/test/mochitest/file_bug662170.html new file mode 100644 index 0000000000..3202b52573 --- /dev/null +++ b/docshell/test/mochitest/file_bug662170.html @@ -0,0 +1,13 @@ +<html> +<body onload='(parent || opener).childLoad()'> + +<div style='height:500px; background:yellow'> +<a id='#top'>Top of the page</a> +</div> + +<div id='bottom'> +<a id='#bottom'>Bottom of the page</a> +</div> + +</body> +</html> diff --git a/docshell/test/mochitest/file_bug668513.html b/docshell/test/mochitest/file_bug668513.html new file mode 100644 index 0000000000..ae417a35bd --- /dev/null +++ b/docshell/test/mochitest/file_bug668513.html @@ -0,0 +1,101 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test file for Bug 668513</title> +<script> + var SimpleTest = opener.SimpleTest; + var ok = opener.ok; + var is = opener.is; + + function finish() { + SimpleTest.finish(); + close(); + } + + function onload_test() { + var win = frames[0]; + ok(win.performance, "Window.performance should be defined"); + ok(win.performance.navigation, "Window.performance.navigation should be defined"); + var navigation = win.performance && win.performance.navigation; + if (navigation === undefined) { + // avoid script errors + finish(); + return; + } + + // do this with a timeout to see the visuals of the navigations. + setTimeout(nav_frame, 100); + } + + var step = 1; + function nav_frame() { + var navigation_frame = frames[0]; + var navigation = navigation_frame.performance.navigation; + switch (step) { + case 1: + { + navigation_frame.location.href = "bug570341_recordevents.html"; + step++; + break; + } + case 2: + { + is(navigation.type, navigation.TYPE_NAVIGATE, + "Expected window.performance.navigation.type == TYPE_NAVIGATE"); + navigation_frame.history.back(); + step++; + break; + } + case 3: + { + is(navigation.type, navigation.TYPE_BACK_FORWARD, + "Expected window.performance.navigation.type == TYPE_BACK_FORWARD"); + step++; + navigation_frame.history.forward(); + break; + } + case 4: + { + is(navigation.type, navigation.TYPE_BACK_FORWARD, + "Expected window.performance.navigation.type == TYPE_BACK_FORWARD"); + navigation_frame.location.href = "bug668513_redirect.html"; + step++; + break; + } + case 5: + { + is(navigation.type, navigation.TYPE_NAVIGATE, + "Expected timing.navigation.type as TYPE_NAVIGATE"); + is(navigation.redirectCount, 1, + "Expected navigation.redirectCount == 1 on an server redirected navigation"); + + var timing = navigation_frame.performance && navigation_frame.performance.timing; + if (timing === undefined) { + // avoid script errors + finish(); + break; + } + ok(timing.navigationStart > 0, "navigationStart should be > 0"); + var sequence = ["navigationStart", "redirectStart", "redirectEnd", "fetchStart"]; + for (var j = 1; j < sequence.length; ++j) { + var prop = sequence[j]; + var prevProp = sequence[j - 1]; + ok(timing[prevProp] <= timing[prop], + ["Expected ", prevProp, " to happen before ", prop, + ", got ", prevProp, " = ", timing[prevProp], + ", ", prop, " = ", timing[prop]].join("")); + } + step++; + finish(); + break; + } + } + } +</script> +</head> +<body> +<div id="frames"> +<iframe name="child0" onload="onload_test();" src="navigation/blank.html"></iframe> +</div> +</body> +</html> diff --git a/docshell/test/mochitest/file_bug669671.sjs b/docshell/test/mochitest/file_bug669671.sjs new file mode 100644 index 0000000000..5871419de8 --- /dev/null +++ b/docshell/test/mochitest/file_bug669671.sjs @@ -0,0 +1,17 @@ +function handleRequest(request, response) { + var count = parseInt(getState("count")); + if (!count || request.queryString == "countreset") { + count = 0; + } + + setState("count", count + 1 + ""); + + response.setHeader("Content-Type", "text/html", false); + response.setHeader("Cache-Control", "max-age=0"); + response.write( + '<html><body onload="opener.onChildLoad()" ' + + "onunload=\"parseInt('0')\">" + + count + + "</body></html>" + ); +} diff --git a/docshell/test/mochitest/file_bug675587.html b/docshell/test/mochitest/file_bug675587.html new file mode 100644 index 0000000000..842ab9ae79 --- /dev/null +++ b/docshell/test/mochitest/file_bug675587.html @@ -0,0 +1 @@ +<script>location.hash = "";</script> diff --git a/docshell/test/mochitest/file_bug680257.html b/docshell/test/mochitest/file_bug680257.html new file mode 100644 index 0000000000..ff480e96a5 --- /dev/null +++ b/docshell/test/mochitest/file_bug680257.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html> +<head> + <style type='text/css'> + a { color: black; } + a:target { color: red; } + </style> +</head> + +<body onload='(opener || parent).popupLoaded()'> + +<a id='a' href='#a'>link</a> +<a id='b' href='#b'>link2</a> + +</body> +</html> diff --git a/docshell/test/mochitest/file_bug703855.html b/docshell/test/mochitest/file_bug703855.html new file mode 100644 index 0000000000..fe15b6e3df --- /dev/null +++ b/docshell/test/mochitest/file_bug703855.html @@ -0,0 +1,2 @@ +<!DOCTYPE html> +<!-- Just need an empty file here, as long as it's served over HTTP --> diff --git a/docshell/test/mochitest/file_bug728939.html b/docshell/test/mochitest/file_bug728939.html new file mode 100644 index 0000000000..1cd52a44e1 --- /dev/null +++ b/docshell/test/mochitest/file_bug728939.html @@ -0,0 +1,3 @@ +<html> +<body onload="opener.popupLoaded()">file_bug728939</body> +</html> diff --git a/docshell/test/mochitest/file_close_onpagehide1.html b/docshell/test/mochitest/file_close_onpagehide1.html new file mode 100644 index 0000000000..ccf3b625a1 --- /dev/null +++ b/docshell/test/mochitest/file_close_onpagehide1.html @@ -0,0 +1,5 @@ +<script> + window.onload = () => { + opener.postMessage("initial", "*"); + }; +</script> diff --git a/docshell/test/mochitest/file_close_onpagehide2.html b/docshell/test/mochitest/file_close_onpagehide2.html new file mode 100644 index 0000000000..a8e9479f47 --- /dev/null +++ b/docshell/test/mochitest/file_close_onpagehide2.html @@ -0,0 +1,5 @@ +<script> + window.onload = () => { + opener.postMessage("second", "*"); + }; +</script>; diff --git a/docshell/test/mochitest/file_compressed_multipart b/docshell/test/mochitest/file_compressed_multipart Binary files differnew file mode 100644 index 0000000000..3c56226951 --- /dev/null +++ b/docshell/test/mochitest/file_compressed_multipart diff --git a/docshell/test/mochitest/file_compressed_multipart^headers^ b/docshell/test/mochitest/file_compressed_multipart^headers^ new file mode 100644 index 0000000000..9376927812 --- /dev/null +++ b/docshell/test/mochitest/file_compressed_multipart^headers^ @@ -0,0 +1,2 @@ +Content-Type: multipart/x-mixed-replace; boundary="testingtesting" +Content-Encoding: gzip diff --git a/docshell/test/mochitest/file_content_javascript_loads_frame.html b/docshell/test/mochitest/file_content_javascript_loads_frame.html new file mode 100644 index 0000000000..9e2851aa8b --- /dev/null +++ b/docshell/test/mochitest/file_content_javascript_loads_frame.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<script type="application/javascript"> +"use strict"; + +addEventListener("message", event => { + if ("ping" in event.data) { + event.source.postMessage({ pong: event.data.ping }, event.origin); + } +}); +</script> +</head> +<body> +</body> +</html> diff --git a/docshell/test/mochitest/file_content_javascript_loads_root.html b/docshell/test/mochitest/file_content_javascript_loads_root.html new file mode 100644 index 0000000000..b9f2c1faa7 --- /dev/null +++ b/docshell/test/mochitest/file_content_javascript_loads_root.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<script type="application/javascript"> +"use strict"; + +window.onload = () => { + opener.postMessage("ready", "*"); +}; + +// eslint-disable-next-line no-shadow +function promiseMessage(source, filter = event => true) { + return new Promise(resolve => { + function listener(event) { + if (event.source == source && filter(event)) { + removeEventListener("message", listener); + resolve(event); + } + } + addEventListener("message", listener); + }); +} + +// Sends a message to the given target window and waits for the response. +function ping(target) { + let msg = { ping: Math.random() }; + target.postMessage(msg, "*"); + return promiseMessage( + target, + event => event.data && event.data.pong == msg.ping + ); +} + +function setFrameLocation(name, uri) { + window[name].location = uri; +} +</script> +</head> +<body> +</body> +</html> diff --git a/docshell/test/mochitest/file_form_restoration_no_store.html b/docshell/test/mochitest/file_form_restoration_no_store.html new file mode 100644 index 0000000000..6e8756a693 --- /dev/null +++ b/docshell/test/mochitest/file_form_restoration_no_store.html @@ -0,0 +1,38 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <script> + window.addEventListener("pageshow", ({ persisted }) => { + let bc = new BroadcastChannel("form_restoration"); + bc.addEventListener("message", ({ data }) => { + switch (data) { + case "enter_data": + document.getElementById("formElement").value = "test"; + break; + case "reload": + bc.close(); + location.reload(); + break; + case "navigate": + bc.close(); + document.location = "file_form_restoration_no_store.html?1"; + break; + case "back": + bc.close(); + history.back(); + break; + case "close": + bc.close(); + window.close(); + break; + } + }); + bc.postMessage({ persisted, formData: document.getElementById("formElement").value }); + }); + </script> +</head> +<body> + <input id="formElement" type="text" value="initial"> +</body> +</html> diff --git a/docshell/test/mochitest/file_form_restoration_no_store.html^headers^ b/docshell/test/mochitest/file_form_restoration_no_store.html^headers^ new file mode 100644 index 0000000000..4030ea1d3d --- /dev/null +++ b/docshell/test/mochitest/file_form_restoration_no_store.html^headers^ @@ -0,0 +1 @@ +Cache-Control: no-store diff --git a/docshell/test/mochitest/file_framedhistoryframes.html b/docshell/test/mochitest/file_framedhistoryframes.html new file mode 100644 index 0000000000..314f9c72d8 --- /dev/null +++ b/docshell/test/mochitest/file_framedhistoryframes.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html> +<body> +<iframe id="iframe" src="historyframes.html"></iframe> +<script type="application/javascript"> + +var SimpleTest = window.opener.SimpleTest; +var is = window.opener.is; + +function done() { + window.opener.done(); +} + +</script> +</body> +</html> diff --git a/docshell/test/mochitest/file_load_during_reload.html b/docshell/test/mochitest/file_load_during_reload.html new file mode 100644 index 0000000000..600d5c1728 --- /dev/null +++ b/docshell/test/mochitest/file_load_during_reload.html @@ -0,0 +1,12 @@ +<!DOCTYPE HTML> +<html> + <head> + <script> + function notifyOpener() { + opener.postMessage("loaded", "*"); + } + </script> + </head> + <body onload="notifyOpener()"> + </body> +</html> diff --git a/docshell/test/mochitest/file_pushState_after_document_open.html b/docshell/test/mochitest/file_pushState_after_document_open.html new file mode 100644 index 0000000000..97a6954f2e --- /dev/null +++ b/docshell/test/mochitest/file_pushState_after_document_open.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<script> + document.addEventListener("DOMContentLoaded", function() { + document.open(); + document.write("<!DOCTYPE html>New Document here"); + document.close(); + // Notify parent via postMessage, since otherwise exceptions will not get + // caught by its onerror handler. + parent.postMessage("doTest", "*"); + }); +</script> diff --git a/docshell/test/mochitest/file_redirect_history.html b/docshell/test/mochitest/file_redirect_history.html new file mode 100644 index 0000000000..3971faf4fd --- /dev/null +++ b/docshell/test/mochitest/file_redirect_history.html @@ -0,0 +1,18 @@ +<html> + <head> + <script> + function loaded() { + addEventListener("message", ({ data }) => { + document.getElementById("form").action = data; + document.getElementById("button").click(); + }, { once: true }); + opener.postMessage("loaded", "*"); + } + </script> + </head> + <body onload="loaded();"> + <form id="form" method="POST"> + <input id="button" type="submit" /> + </form> + </body> +</html> diff --git a/docshell/test/mochitest/form_submit.sjs b/docshell/test/mochitest/form_submit.sjs new file mode 100644 index 0000000000..1a1fa5d89c --- /dev/null +++ b/docshell/test/mochitest/form_submit.sjs @@ -0,0 +1,40 @@ +"use strict"; + +const CC = Components.Constructor; +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); + +const BinaryOutputStream = CC( + "@mozilla.org/binaryoutputstream;1", + "nsIBinaryOutputStream", + "setOutputStream" +); + +function log(str) { + // dump(`LOG: ${str}\n`); +} + +async function handleRequest(request, response) { + if (request.method !== "POST") { + throw new Error("Expected a post request"); + } else { + log("Reading request"); + let available = 0; + let inputStream = new BinaryInputStream(request.bodyInputStream); + while ((available = inputStream.available()) > 0) { + log(inputStream.readBytes(available)); + } + } + + log("Setting Headers"); + response.setHeader("Content-Type", "text/html", false); + response.setStatusLine(request.httpVersion, "200", "OK"); + log("Writing body"); + response.write( + '<script>"use strict"; let target = opener ? opener : parent; target.postMessage("done", "*");</script>' + ); + log("Done"); +} diff --git a/docshell/test/mochitest/form_submit_redirect.sjs b/docshell/test/mochitest/form_submit_redirect.sjs new file mode 100644 index 0000000000..dbc32b9643 --- /dev/null +++ b/docshell/test/mochitest/form_submit_redirect.sjs @@ -0,0 +1,15 @@ +"use strict"; + +Cu.importGlobalProperties(["URLSearchParams"]); + +async function handleRequest(request, response) { + if (request.method !== "POST") { + throw new Error("Expected a post request"); + } else { + let params = new URLSearchParams(request.queryString); + let redirect = params.get("redirectTo"); + + response.setStatusLine(request.httpVersion, 302, "Moved Temporarily"); + response.setHeader("Location", redirect); + } +} diff --git a/docshell/test/mochitest/historyframes.html b/docshell/test/mochitest/historyframes.html new file mode 100644 index 0000000000..846cdd9f9d --- /dev/null +++ b/docshell/test/mochitest/historyframes.html @@ -0,0 +1,176 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=602256 +--> +<head> + <title>Test for Bug 602256</title> +</head> +<body onload="SimpleTest.executeSoon(run_test)"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602256">Mozilla Bug 602256</a> +<div id="content"> + <iframe id="iframe" src="start_historyframe.html"></iframe> +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 602256 **/ + +var testWin = window.opener ? window.opener : window.parent; + +var SimpleTest = testWin.SimpleTest; +function is() { testWin.is.apply(testWin, arguments); } + +var gFrame = null; + +function gState() { + return location.hash.replace(/^#/, ""); +} + +function waitForLoad(aCallback) { + function listener() { + gFrame.removeEventListener("load", listener); + SimpleTest.executeSoon(aCallback); + } + + gFrame.addEventListener("load", listener); +} + +function loadContent(aURL, aCallback) { + waitForLoad(aCallback); + + gFrame.src = aURL; +} + +function getURL() { + return gFrame.contentDocument.documentURI; +} + +function getContent() { + return gFrame.contentDocument.getElementById("text").textContent; +} + +var BASE_URI = "http://mochi.test:8888/tests/docshell/test/mochitest/"; +var START = BASE_URI + "start_historyframe.html"; +var URL1 = BASE_URI + "url1_historyframe.html"; +var URL2 = BASE_URI + "url2_historyframe.html"; + +function run_test() { + window.location.hash = "START"; + + gFrame = document.getElementById("iframe"); + + test_basic_inner_navigation(); +} + +function end_test() { + testWin.done(); +} + +var gTestContinuation = null; +function continueAsync() { + setTimeout(function() { gTestContinuation.next(); }) +} + +function test_basic_inner_navigation() { + // Navigate the inner frame a few times + loadContent(URL1, function() { + is(getURL(), URL1, "URL should be correct"); + is(getContent(), "Test1", "Page should be correct"); + + loadContent(URL2, function() { + is(getURL(), URL2, "URL should be correct"); + is(getContent(), "Test2", "Page should be correct"); + + // Test that history is working + waitForLoad(function() { + is(getURL(), URL1, "URL should be correct"); + is(getContent(), "Test1", "Page should be correct"); + + waitForLoad(function() { + is(getURL(), URL2, "URL should be correct"); + is(getContent(), "Test2", "Page should be correct"); + + gTestContinuation = test_state_navigation(); + gTestContinuation.next(); + }); + window.history.forward(); + }); + window.history.back(); + }); + }); +} + +function* test_state_navigation() { + window.location.hash = "STATE1"; + + is(getURL(), URL2, "URL should be correct"); + is(getContent(), "Test2", "Page should be correct"); + + window.location.hash = "STATE2"; + + is(getURL(), URL2, "URL should be correct"); + is(getContent(), "Test2", "Page should be correct"); + + window.addEventListener("popstate", (e) => { + continueAsync(); + }, {once: true}); + window.history.back(); + yield; + + is(gState(), "STATE1", "State should be correct after going back"); + is(getURL(), URL2, "URL should be correct"); + is(getContent(), "Test2", "Page should be correct"); + + window.addEventListener("popstate", (e) => { + continueAsync(); + }, {once: true}); + window.history.forward(); + yield; + + is(gState(), "STATE2", "State should be correct after going forward"); + is(getURL(), URL2, "URL should be correct"); + is(getContent(), "Test2", "Page should be correct"); + + window.addEventListener("popstate", (e) => { + continueAsync(); + }, {once: true}); + window.history.back(); + yield; + + window.addEventListener("popstate", (e) => { + continueAsync(); + }, {once: true}); + window.history.back(); + yield; + + is(gState(), "START", "State should be correct"); + is(getURL(), URL2, "URL should be correct"); + is(getContent(), "Test2", "Page should be correct"); + + waitForLoad(function() { + is(getURL(), URL1, "URL should be correct"); + is(getContent(), "Test1", "Page should be correct"); + + waitForLoad(function() { + is(gState(), "START", "State should be correct"); + is(getURL(), START, "URL should be correct"); + is(getContent(), "Start", "Page should be correct"); + + end_test(); + }); + + window.history.back(); + + is(gState(), "START", "State should be correct after going back twice"); + }); + + window.history.back(); + continueAsync(); + yield; + is(gState(), "START", "State should be correct"); +} +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/mochitest.ini b/docshell/test/mochitest/mochitest.ini new file mode 100644 index 0000000000..b18e1afb16 --- /dev/null +++ b/docshell/test/mochitest/mochitest.ini @@ -0,0 +1,174 @@ +[DEFAULT] +support-files = + bug404548-subframe.html + bug404548-subframe_window.html + bug413310-post.sjs + bug413310-subframe.html + bug529119-window.html + bug570341_recordevents.html + bug668513_redirect.html + bug668513_redirect.html^headers^ + bug691547_frame.html + dummy_page.html + file_anchor_scroll_after_document_open.html + file_bfcache_plus_hash_1.html + file_bfcache_plus_hash_2.html + file_bug385434_1.html + file_bug385434_2.html + file_bug385434_3.html + file_bug475636.sjs + file_bug509055.html + file_bug540462.html + file_bug580069_1.html + file_bug580069_2.sjs + file_bug598895_1.html + file_bug598895_2.html + file_bug590573_1.html + file_bug590573_2.html + file_bug634834.html + file_bug637644_1.html + file_bug637644_2.html + file_bug640387.html + file_bug653741.html + file_bug660404 + file_bug660404^headers^ + file_bug660404-1.html + file_bug662170.html + file_bug669671.sjs + file_bug680257.html + file_bug703855.html + file_bug728939.html + file_bug1121701_1.html + file_bug1121701_2.html + file_bug1186774.html + file_bug1151421.html + file_bug1450164.html + file_close_onpagehide1.html + file_close_onpagehide2.html + file_compressed_multipart + file_compressed_multipart^headers^ + file_pushState_after_document_open.html + historyframes.html + ping.html + start_historyframe.html + url1_historyframe.html + url2_historyframe.html + +[test_anchor_scroll_after_document_open.html] +[test_bfcache_plus_hash.html] +[test_bug1422334.html] +support-files = + bug1422334_redirect.html + bug1422334_redirect.html^headers^ + !/docshell/test/navigation/blank.html +[test_bug385434.html] +[test_bug387979.html] +[test_bug402210.html] +[test_bug404548.html] +[test_bug413310.html] +skip-if = true +# Disabled for too many intermittent failures (bug 719186) +[test_bug475636.html] +[test_bug509055.html] +[test_bug511449.html] +skip-if = toolkit != "cocoa" || headless # Headless: bug 1410525 +support-files = file_bug511449.html +[test_bug529119-1.html] +[test_bug529119-2.html] +[test_bug530396.html] +support-files = bug530396-noref.sjs bug530396-subframe.html +[test_bug540462.html] +skip-if = toolkit == 'android' && debug && !is_fennec +[test_bug551225.html] +[test_bug570341.html] +skip-if = (verify && !debug && (os == 'win')) +[test_bug580069.html] +skip-if = (verify && !debug && (os == 'win')) +[test_bug590573.html] +[test_bug598895.html] +[test_bug634834.html] +[test_bug637644.html] +[test_bug640387_1.html] +[test_bug640387_2.html] +[test_bug653741.html] +[test_bug660404.html] +[test_bug662170.html] +[test_bug668513.html] +support-files = file_bug668513.html +[test_bug669671.html] +[test_bug675587.html] +support-files = file_bug675587.html +[test_bug680257.html] +[test_bug691547.html] +[test_bug694612.html] +[test_bug703855.html] +[test_bug728939.html] +[test_bug797909.html] +[test_bug1045096.html] +[test_bug1121701.html] +[test_bug1151421.html] +[test_bug1186774.html] +[test_bug1450164.html] +[test_bug1507702.html] +[test_bug1645781.html] +support-files = + form_submit.sjs +[test_bug1729662.html] +support-files = + file_bug1729662.html +[test_bug1740516.html] +support-files = + file_bug1740516_1.html + file_bug1740516_1_inner.html + file_bug1740516_2.html +[test_form_restoration.html] +support-files = + file_form_restoration_no_store.html + file_form_restoration_no_store.html^headers^ +[test_bug1741132.html] +support-files = + file_bug1741132.html +skip-if = toolkit == "android" && !sessionHistoryInParent +[test_bug1742865.html] +support-files = + file_bug1742865.sjs + file_bug1742865_outer.sjs +skip-if = toolkit == "android" && debug && fission && verify # Bug 1745937 +[test_bug1743353.html] +support-files = + file_bug1743353.html +[test_bug1747033.html] +support-files = + file_bug1747033.sjs +[test_bug1773192.html] +support-files = + file_bug1773192_1.html + file_bug1773192_2.html + file_bug1773192_3.sjs +[test_close_onpagehide_by_history_back.html] +[test_close_onpagehide_by_window_close.html] +[test_compressed_multipart.html] +[test_content_javascript_loads.html] +support-files = + file_content_javascript_loads_root.html + file_content_javascript_loads_frame.html +[test_forceinheritprincipal_overrule_owner.html] +[test_framedhistoryframes.html] +support-files = file_framedhistoryframes.html +[test_load_during_reload.html] +support-files = file_load_during_reload.html +[test_pushState_after_document_open.html] +[test_navigate_after_pagehide.html] +[test_redirect_history.html] +support-files = + file_redirect_history.html + form_submit_redirect.sjs +[test_windowedhistoryframes.html] +skip-if = (!debug && os == 'android') # Bug 1573892 +[test_triggeringprincipal_location_seturi.html] +[test_double_submit.html] +support-files = + clicker.html + double_submit.sjs +[test_iframe_srcdoc_to_remote.html] +[test_javascript_sandboxed_popup.html] diff --git a/docshell/test/mochitest/ping.html b/docshell/test/mochitest/ping.html new file mode 100644 index 0000000000..7d84560dd1 --- /dev/null +++ b/docshell/test/mochitest/ping.html @@ -0,0 +1,6 @@ +<!doctype html> +<script> + "use strict"; + let target = (window.opener || window.parent); + target.postMessage("ping", "*"); +</script> diff --git a/docshell/test/mochitest/start_historyframe.html b/docshell/test/mochitest/start_historyframe.html new file mode 100644 index 0000000000..a791af4e64 --- /dev/null +++ b/docshell/test/mochitest/start_historyframe.html @@ -0,0 +1 @@ +<p id='text'>Start</p> diff --git a/docshell/test/mochitest/test_anchor_scroll_after_document_open.html b/docshell/test/mochitest/test_anchor_scroll_after_document_open.html new file mode 100644 index 0000000000..6e415e3cbe --- /dev/null +++ b/docshell/test/mochitest/test_anchor_scroll_after_document_open.html @@ -0,0 +1,55 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=881487 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 881487</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 881487 **/ + SimpleTest.waitForExplicitFinish(); + // Child needs to invoke us, otherwise our onload will fire before the child + // has done the write/close bit. + var gotOnload = false; + addLoadEvent(function() { + gotOnload = true; + }); + onmessage = function handleMessage(msg) { + if (msg.data == "doTest") { + if (!gotOnload) { + addLoadEvent(function() { handleMessage(msg); }); + return; + } + frames[0].onscroll = function() { + ok(true, "Got a scroll event"); + SimpleTest.finish(); + }; + frames[0].location.hash = "#target"; + return; + } + if (msg.data == "haveHash") { + ok(false, "Child got reloaded"); + } else { + ok(false, "Unexpected message"); + } + SimpleTest.finish(); + }; + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=881487">Mozilla Bug 881487</a> +<p id="display"> + <!-- iframe goes here so it can scroll --> +<iframe src="file_anchor_scroll_after_document_open.html"></iframe> +</p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bfcache_plus_hash.html b/docshell/test/mochitest/test_bfcache_plus_hash.html new file mode 100644 index 0000000000..66871fcbbf --- /dev/null +++ b/docshell/test/mochitest/test_bfcache_plus_hash.html @@ -0,0 +1,153 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=646641 +--> +<head> + <title>Test for Bug 646641</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/WindowSnapshot.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=646641">Mozilla Bug 646641</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 646641 **/ + +/** + * Steps: + * - Main page (this one) opens file_bfcache_plus_hash_1.html (subpage 1) + * - subpage 1 sends msg { "childLoad", 1 } + * - subpage 1 sends msg { "childPageshow", 1 } + * - main page sends message "pushState" + * - subpage 1 does pushState() + * - subpage 1 navigates to file_bfcache_plus_hash_2.html (subpage 2) + * - subpage 2 sends msg { "childLoad", 2 } + * - subpage 2 sends msg { "childPageshow", 2 } + * - main page sends msg "go-2" + * - subpage 2 goes back two history entries + * - subpage 1 sends msg { "childPageshow", 1 } + * - Receiving only this msg shows we have retrieved the document from bfcache + * - main page sends msg "close" + * - subpage 1 sends msg "closed" + */ +SimpleTest.waitForExplicitFinish(); + +function debug(msg) { + // Wrap dump so we can turn debug messages on and off easily. + dump(msg + "\n"); +} + +var expectedLoadNum = -1; +var expectedPageshowNum = -1; + +function waitForLoad(n) { + debug("Waiting for load " + n); + expectedLoadNum = n; +} + +function waitForShow(n) { + debug("Waiting for show " + n); + expectedPageshowNum = n; +} + + + +function executeTest() { + function* test() { + window.open("file_bfcache_plus_hash_1.html", "", "noopener"); + waitForLoad(1); + waitForShow(1); + yield undefined; + yield undefined; + + bc1.postMessage("pushState"); + + waitForLoad(2); + waitForShow(2); + yield undefined; + yield undefined; + + // Now go back 2. The first page should be retrieved from bfcache. + bc2.postMessage("go-2"); + waitForShow(1); + yield undefined; + + bc1.postMessage("close"); + } + + var bc1 = new BroadcastChannel("bug646641_1"); + var bc2 = new BroadcastChannel("bug646641_2"); + bc1.onmessage = (msgEvent) => { + var msg = msgEvent.data.message; + var n = msgEvent.data.num; + if (msg == "childLoad") { + if (n == expectedLoadNum) { + debug("Got load " + n); + expectedLoadNum = -1; + + // Spin the event loop before calling gGen.next() so the generator runs + // outside the onload handler. This prevents us from encountering all + // sorts of docshell quirks. + setTimeout(function() { gGen.next(); }, 0); + } else { + debug("Got unexpected load " + n); + ok(false, "Got unexpected load " + n); + } + } else if (msg == "childPageshow") { + if (n == expectedPageshowNum) { + debug("Got expected pageshow " + n); + expectedPageshowNum = -1; + ok(true, "Got expected pageshow " + n); + setTimeout(function() { gGen.next(); }, 0); + } else { + debug("Got unexpected pageshow " + n); + ok(false, "Got unexpected pageshow " + n); + } + } else if (msg == "closed") { + bc1.close(); + bc2.close(); + SimpleTest.finish(); + } + } + + bc2.onmessage = bc1.onmessage; + + var gGen = test(); + + // If Fission is disabled, the pref is no-op. + SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => { + gGen.next(); + }); +} +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> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug1045096.html b/docshell/test/mochitest/test_bug1045096.html new file mode 100644 index 0000000000..e9b8df7a5f --- /dev/null +++ b/docshell/test/mochitest/test_bug1045096.html @@ -0,0 +1,29 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1045096 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1045096</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=1045096">Mozilla Bug 1045096</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> + <script type="application/javascript"> + + /** Test for Bug 1045096 **/ + var i = document.createElement("iframe"); + i.src = "javascript:false"; // This is required! + $("content").appendChild(i); + ok(i.contentWindow.performance, "Should have a performance object"); + </script> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug1121701.html b/docshell/test/mochitest/test_bug1121701.html new file mode 100644 index 0000000000..66391d1f5b --- /dev/null +++ b/docshell/test/mochitest/test_bug1121701.html @@ -0,0 +1,108 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1121701 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1121701</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 1121701 **/ + SimpleTest.waitForExplicitFinish(); + + var testUrl1 = "file_bug1121701_1.html"; + var testUrl2 = "file_bug1121701_2.html"; + + var page1LoadCount = 0; + let page1Done = {}; + page1Done.promise = new Promise(resolve => { + page1Done.resolve = resolve; + }); + let page2Done = {}; + page2Done.promise = new Promise(resolve => { + page2Done.resolve = resolve; + }); + + addLoadEvent(async function() { + + // 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. + if (isXOrigin) { + await SpecialPowers.pushPrefEnv({ + set: [["privacy.partition.always_partition_third_party_non_cookie_storage", false]], + }); + SpecialPowers.wrap(document).notifyUserGestureActivation(); + await SpecialPowers.addPermission("storageAccessAPI", true, window.location.href); + await SpecialPowers.wrap(document).requestStorageAccess(); + } + + var bc = new BroadcastChannel("file_bug1121701_1"); + var bc2 = new BroadcastChannel("file_bug1121701_2"); + + async function scheduleFinish() { + await Promise.all([page1Done.promise, page2Done.promise]); + bc2.close(); + bc.close(); + SimpleTest.finish(); + } + + bc.onmessage = (msgEvent) => { + var msg = msgEvent.data; + var command = msg.command; + if (command == "child1PageShow") { + ++page1LoadCount; + var persisted = msg.persisted; + var pageHideAsserts = msg.pageHideAsserts; + if (pageHideAsserts) { + ok(pageHideAsserts.persisted, "onpagehide: test page 1 should get persisted"); + is(pageHideAsserts.innerHTML, "modified", "onpagehide: innerHTML text is 'modified'"); + } + if (page1LoadCount == 1) { + SimpleTest.executeSoon(function() { + is(persisted, false, "Initial page load shouldn't be persisted."); + bc.postMessage({command: "setInnerHTML", testUrl2}); + }); + } else if (page1LoadCount == 2) { + is(persisted, true, "Page load from bfcache should be persisted."); + is(msg.innerHTML, "modified", "innerHTML text is 'modified'"); + bc.postMessage({command: "close"}); + } + } else if (command == "closed") { + page1Done.resolve(); + } + } + bc2.onmessage = (msgEvent) => { + var msg = msgEvent.data; + var command = msg.command; + if (command == "child2PageShow") { + bc2.postMessage({command: "setInnerHTML", location: location.href}); + } else if (command == "onmessage") { + page2Done.resolve(); + } + } + + scheduleFinish(); + // If Fission is disabled, the pref is no-op. + SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => { + window.open(testUrl1, "", "noopener"); + }); + }); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1121701">Mozilla Bug 1121701</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug1151421.html b/docshell/test/mochitest/test_bug1151421.html new file mode 100644 index 0000000000..4eb647e121 --- /dev/null +++ b/docshell/test/mochitest/test_bug1151421.html @@ -0,0 +1,61 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1151421 +--> +<head> + <title>Test for Bug 1151421</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=1151421">Mozilla Bug 1151421</a> + +<script type="application/javascript"> + +/** Test for Bug 1151421 **/ +SimpleTest.waitForExplicitFinish(); + +function childLoad() { + // Spin the event loop so we leave the onload handler. + SimpleTest.executeSoon(childLoad2); +} + +function childLoad2() { + let iframe = document.getElementById("iframe"); + let cw = iframe.contentWindow; + let content = cw.document.getElementById("content"); + + // Create a function to calculate an invariant. + let topPlusOffset = function() { + return Math.round(content.getBoundingClientRect().top + cw.pageYOffset); + }; + + let initialTPO = topPlusOffset(); + + // Scroll the iframe to various positions, and check the TPO. + // Scrolling down to the bottom will adjust the page offset by a fractional amount. + let positions = [-100, 0.17, 0, 1.5, 10.41, 1e6, 12.1]; + + // Run some tests with scrollTo() and ensure we have the same invariant after scrolling. + positions.forEach(function(pos) { + cw.scrollTo(0, pos); + is(topPlusOffset(), initialTPO, "Top plus offset should remain invariant across scrolling."); + }); + + positions.reverse().forEach(function(pos) { + cw.scrollTo(0, pos); + is(topPlusOffset(), initialTPO, "(reverse) Top plus offset should remain invariant across scrolling."); + }); + + SimpleTest.finish(); +} + +</script> + +<!-- When the iframe loads, it calls childLoad(). --> +<br> +<iframe height='100px' id='iframe' src='file_bug1151421.html'></iframe> + +</body> +</html> diff --git a/docshell/test/mochitest/test_bug1186774.html b/docshell/test/mochitest/test_bug1186774.html new file mode 100644 index 0000000000..afaf3b0f34 --- /dev/null +++ b/docshell/test/mochitest/test_bug1186774.html @@ -0,0 +1,51 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1186774 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1186774</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 1186774 **/ + +var child; + +function runTest() { + child = window.open("file_bug1186774.html", "", "width=100,height=100"); + child.onload = function() { + setTimeout(function() { + child.scrollTo(0, 0); + child.history.pushState({}, "initial"); + child.scrollTo(0, 3000); + child.history.pushState({}, "scrolled"); + child.scrollTo(0, 6000); + child.history.back(); + }); + }; + + child.onpopstate = function() { + is(Math.round(child.scrollY), 6000, "Shouldn't have scrolled before popstate"); + child.close(); + SimpleTest.finish(); + }; +} + +SimpleTest.waitForExplicitFinish(); +addLoadEvent(runTest); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1186774">Mozilla Bug 1186774</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug1422334.html b/docshell/test/mochitest/test_bug1422334.html new file mode 100644 index 0000000000..b525ae1d9c --- /dev/null +++ b/docshell/test/mochitest/test_bug1422334.html @@ -0,0 +1,40 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Ensure that reload after replaceState after 3xx redirect does the right thing.</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <script> + SimpleTest.waitForExplicitFinish(); + addLoadEvent(function() { + var ifr = document.querySelector("iframe"); + var win = ifr.contentWindow; + is(win.location.href, location.href.replace(location.search, "") + .replace("mochitest/test_bug1422334.html", + "navigation/blank.html?x=y"), + "Should have the right location on initial load"); + + win.history.replaceState(null, '', win.location.pathname); + is(win.location.href, location.href.replace(location.search, "") + .replace("mochitest/test_bug1422334.html", + "navigation/blank.html"), + "Should have the right location after replaceState call"); + + ifr.onload = function() { + is(win.location.href, location.href.replace(location.search, "") + .replace("mochitest/test_bug1422334.html", + "navigation/blank.html"), + "Should have the right location after reload"); + SimpleTest.finish(); + } + win.location.reload(); + }); + </script> +</head> +<body> +<p id="display"><iframe src="bug1422334_redirect.html"></iframe></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug1450164.html b/docshell/test/mochitest/test_bug1450164.html new file mode 100644 index 0000000000..190b2f349b --- /dev/null +++ b/docshell/test/mochitest/test_bug1450164.html @@ -0,0 +1,31 @@ +<!DOCTYPE HTML> +<html> + <!-- + https://bugzilla.mozilla.org/show_bug.cgi?id=1450164 + --> + <head> + <meta charset="utf-8"> + <title>Test for Bug 1450164</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 1450164 **/ + + function runTest() { + var child = window.open("file_bug1450164.html", "", "width=100,height=100"); + child.onload = function() { + // After the window loads, close it. If we don't crash in debug, consider that a pass. + child.close(); + }; + } + + SimpleTest.waitForExplicitFinish(); + addLoadEvent(runTest); + + </script> + </head> + <body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1450164">Mozilla Bug 1450164</a> + </body> +</html> diff --git a/docshell/test/mochitest/test_bug1507702.html b/docshell/test/mochitest/test_bug1507702.html new file mode 100644 index 0000000000..fd88ee60a5 --- /dev/null +++ b/docshell/test/mochitest/test_bug1507702.html @@ -0,0 +1,57 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1507702 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1507702</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <link rel="icon" href="about:crashparent"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1507702">Mozilla Bug 1507702</a> +<img src="about:crashparent"> +<img src="about:crashcontent"> +<iframe src="about:crashparent"></iframe> +<iframe src="about:crashcontent"></iframe> +<script> + let urls = ["about:crashparent", "about:crashcontent"]; + async function testFetch() { + const url = urls.shift(); + if (!url) { + return Promise.resolve(); + } + + let threw; + try { + await fetch(url); + threw = false; + } catch (e) { + threw = true; + } + + ok(threw === true, "fetch should reject"); + return testFetch(); + } + + document.body.onload = async () => { + for (const url of ["about:crashparent", "about:crashcontent"]) { + SimpleTest.doesThrow(() => { + top.location.href = url; + }, "navigation should throw"); + + SimpleTest.doesThrow(() => { + location.href = url; + }, "navigation should throw"); + } + + await testFetch(); + SimpleTest.finish(); + }; + + SimpleTest.waitForExplicitFinish(); +</script> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug1645781.html b/docshell/test/mochitest/test_bug1645781.html new file mode 100644 index 0000000000..6cf676b7a9 --- /dev/null +++ b/docshell/test/mochitest/test_bug1645781.html @@ -0,0 +1,90 @@ +<!doctype html> +<html> + <head> + <title>Test for Bug 1590762</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> + <form id="form" action="form_submit.sjs" method="POST" target="targetFrame"> + <input id="input" type="text" name="name" value=""> + <input id="button" type="submit"> + </form> + <script> + "use strict"; + const PATH = "/tests/docshell/test/mochitest/"; + const SAME_ORIGIN = new URL(PATH, window.location.origin);; + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + const CROSS_ORIGIN_1 = new URL(PATH, "http://test1.example.com/"); + const CROSS_ORIGIN_2 = new URL(PATH, "https://example.com/"); + const TARGET = "ping.html"; + const ACTION = "form_submit.sjs"; + + function generateBody(size) { + let data = new Uint8Array(size); + for (let i = 0; i < size; ++i) { + data[i] = 97 + Math.random() * (123 - 97); + } + + return new TextDecoder().decode(data); + } + + async function withFrame(url) { + info("Creating frame"); + let frame = document.createElement('iframe'); + frame.name = "targetFrame"; + + return new Promise(resolve => { + addEventListener('message', async function({source}) { + info("Frame loaded"); + if (frame.contentWindow == source) { + resolve(frame); + } + }, { once: true }); + frame.src = url; + document.body.appendChild(frame); + }); + } + + function click() { + synthesizeMouse(document.getElementById('button'), 5, 5, {}); + } + + function* spec() { + let urls = [SAME_ORIGIN, CROSS_ORIGIN_1, CROSS_ORIGIN_2]; + for (let action of urls) { + for (let target of urls) { + yield { action: new URL(ACTION, action), + target: new URL(TARGET, target) }; + } + } + } + + info("Starting tests"); + let form = document.getElementById('form'); + + // The body of the POST needs to be large to trigger this. + // 1024*1024 seems to be enough, but scaling to get a margin. + document.getElementById('input').value = generateBody(1024*1024); + for (let { target, action } of spec()) { + add_task(async function runTest() { + info(`Running test ${target} with ${action}`); + form.action = action; + let frame = await withFrame(target); + await new Promise(resolve => { + addEventListener('message', async function() { + info("Form loaded"); + frame.remove(); + resolve(); + }, { once: true }); + + click(); + }); + + ok(true, `Submitted to ${origin} with target ${action}`) + }); + }; + </script> + </body> +</html> diff --git a/docshell/test/mochitest/test_bug1729662.html b/docshell/test/mochitest/test_bug1729662.html new file mode 100644 index 0000000000..ec43508494 --- /dev/null +++ b/docshell/test/mochitest/test_bug1729662.html @@ -0,0 +1,76 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test back/forward after pushState</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <script> + SimpleTest.waitForExplicitFinish(); + SimpleTest.requestFlakyTimeout("Need to wait to make sure an event does not fire"); + + async function runTest() { + let win = window.open(); + let goneBackAndForwardOnce = new Promise((resolve) => { + let timeoutID; + + // We should only get one load event in win. + let bc = new BroadcastChannel("bug1729662"); + bc.addEventListener("message", () => { + bc.addEventListener("message", () => { + clearTimeout(timeoutID); + resolve(false); + }); + }, { once: true }); + + let goneBack = false, goneForward = false; + win.addEventListener("popstate", ({ state }) => { + // We should only go back and forward once, if we get another + // popstate after that then we should fall through to the + // failure case below. + if (!(goneBack && goneForward)) { + // Check if this is the popstate for the forward (the one for + // back will have state == undefined). + if (state == 1) { + ok(goneBack, "We should have gone back before going forward"); + + goneForward = true; + + // Wait a bit to make sure there are no more popstate events. + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + timeoutID = setTimeout(resolve, 1000, true); + + return; + } + + // Check if we've gone back once before, if we get another + // popstate after that then we should fall through to the + // failure case below. + if (!goneBack) { + goneBack = true; + + return; + } + } + + clearTimeout(timeoutID); + resolve(false); + }); + }); + + win.location = "file_bug1729662.html"; + + ok(await goneBackAndForwardOnce, "Stopped navigating history"); + + win.close(); + + SimpleTest.finish(); + } + </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/mochitest/test_bug1740516.html b/docshell/test/mochitest/test_bug1740516.html new file mode 100644 index 0000000000..b54932c736 --- /dev/null +++ b/docshell/test/mochitest/test_bug1740516.html @@ -0,0 +1,79 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test pageshow event order for iframe</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <script> + SimpleTest.waitForExplicitFinish(); + + function waitForPageShow(outer, inner) { + return new Promise((resolve) => { + let results = []; + outer.addEventListener("message", ({ data: persisted }) => { + results.push({ name: outer.name, persisted }); + if (results.length == 2) { + resolve(results); + } + }, { once: true }); + inner.addEventListener("message", ({ data: persisted }) => { + results.push({ name: inner.name, persisted }); + if (results.length == 2) { + resolve(results); + } + }, { once: true }); + }); + } + async function runTest() { + let outerBC = new BroadcastChannel("bug1740516_1"); + let innerBC = new BroadcastChannel("bug1740516_1_inner"); + + let check = waitForPageShow(outerBC, innerBC).then(([first, second]) => { + is(first.name, "bug1740516_1_inner", "Should get pageShow from inner iframe page first."); + ok(!first.persisted, "First navigation shouldn't come from BFCache."); + is(second.name, "bug1740516_1", "Should get pageShow from outer page second."); + ok(!second.persisted, "First navigation shouldn't come from BFCache."); + }, () => { + ok(false, "The promises should not be rejected."); + }); + window.open("file_bug1740516_1.html", "", "noopener"); + await check; + + check = waitForPageShow(outerBC, innerBC).then(([first, second]) => { + is(first.name, "bug1740516_1_inner", "Should get pageShow from inner iframe page first."); + ok(first.persisted, "Second navigation should come from BFCache"); + is(second.name, "bug1740516_1", "Should get pageShow from outer page second."); + ok(second.persisted, "Second navigation should come from BFCache"); + }, () => { + ok(false, "The promises should not be rejected."); + }); + outerBC.postMessage("navigate"); + await check; + + check = waitForPageShow(outerBC, innerBC).then(([first, second]) => { + is(first.name, "bug1740516_1_inner", "Should get pageShow from inner iframe page first."); + ok(!first.persisted, "Third navigation should not come from BFCache"); + is(second.name, "bug1740516_1", "Should get pageShow from outer page second."); + ok(!second.persisted, "Third navigation should not come from BFCache"); + }, () => { + ok(false, "The promises should not be rejected."); + }); + outerBC.postMessage("block_bfcache_and_navigate"); + await check; + + outerBC.postMessage("close"); + + outerBC.close(); + innerBC.close(); + + SimpleTest.finish(); + } + </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/mochitest/test_bug1741132.html b/docshell/test/mochitest/test_bug1741132.html new file mode 100644 index 0000000000..1ae9727d9c --- /dev/null +++ b/docshell/test/mochitest/test_bug1741132.html @@ -0,0 +1,79 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test form restoration for no-store pages</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <script> + // The number of entries which we keep in the BFCache (see nsSHistory.h). + const VIEWER_WINDOW = 3; + + SimpleTest.waitForExplicitFinish(); + + function runTest() { + let bc = new BroadcastChannel("bug1741132"); + + // Setting the pref to 0 should evict all content viewers. + let load = SpecialPowers.pushPrefEnv({ + set: [["browser.sessionhistory.max_total_viewers", 0]], + }).then(() => { + // Set the pref to VIEWER_WINDOW + 2 now, to be sure we + // could fit all entries. + SpecialPowers.pushPrefEnv({ + set: [["browser.sessionhistory.max_total_viewers", VIEWER_WINDOW + 2]], + }); + }).then(() => { + return new Promise(resolve => { + bc.addEventListener("message", resolve, { once: true }); + window.open("file_bug1741132.html", "", "noopener"); + }); + }); + // We want to try to keep one entry too many in the BFCache, + // so we ensure that there's at least VIEWER_WINDOW + 2 + // entries in session history (with one for the displayed + // page). + for (let i = 0; i < VIEWER_WINDOW + 2; ++i) { + load = load.then(() => { + return new Promise((resolve) => { + bc.addEventListener("message", resolve, { once: true }); + bc.postMessage({ cmd: "load", arg: `file_bug1741132.html?${i}` }); + }); + }); + } + load.then(() => { + return new Promise((resolve) => { + bc.addEventListener("message", ({ data: persisted }) => { + resolve(persisted); + }, { once: true }); + // Go back past the first entry that should be in the BFCache. + bc.postMessage({ cmd: "go", arg: -(VIEWER_WINDOW + 1) }); + }); + }).then((persisted) => { + ok(!persisted, "Only 3 pages should be kept in the BFCache"); + }).then(() => { + return new Promise((resolve) => { + bc.addEventListener("message", ({ data: persisted }) => { + resolve(persisted); + }, { once: true }); + // Go forward to the first entry that should be in the BFCache. + bc.postMessage({ cmd: "go", arg: 1 }); + }); + }).then((persisted) => { + ok(persisted, "3 pages should be kept in the BFCache"); + + bc.postMessage("close"); + + bc.close(); + + SimpleTest.finish(); + }); + } + </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/mochitest/test_bug1742865.html b/docshell/test/mochitest/test_bug1742865.html new file mode 100644 index 0000000000..c8f9a4eca3 --- /dev/null +++ b/docshell/test/mochitest/test_bug1742865.html @@ -0,0 +1,137 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Auto refreshing pages shouldn't add an entry to session history</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <script> + const REFRESH_REDIRECT_TIMER = 15; + + // 2 tests (same and cross origin) consisting of 2 refreshes of maximum 1 seconds + // 2 tests (same and cross origin) consisting of 2 refreshes of REFRESH_REDIRECT_TIMER seconds + // => We need (2 * 1) + (2 * 15) seconds + SimpleTest.requestLongerTimeout(3); + SimpleTest.waitForExplicitFinish(); + + const SJS = new URL("file_bug1742865.sjs", location.href); + const SJS_OUTER = new URL("file_bug1742865_outer.sjs", location.href); + const SCROLL = 500; + + let tolerance; + function setup() { + return SpecialPowers.spawn(window.top, [], () => { + return SpecialPowers.getDOMWindowUtils(content.window).getResolution(); + }).then(resolution => { + // Allow a half pixel difference if the top document's resolution is lower + // than 1.0 because the scroll position is aligned with screen pixels + // instead of CSS pixels. + tolerance = resolution < 1.0 ? 0.5 : 0.0; + }); + } + + function checkScrollPosition(scrollPosition, shouldKeepScrollPosition) { + isfuzzy(scrollPosition, shouldKeepScrollPosition ? SCROLL : 0, tolerance, + `Scroll position ${shouldKeepScrollPosition ? "should" : "shouldn't"} be maintained for meta refresh`); + } + + function openWindowAndCheckRefresh(url, params, shouldAddToHistory, shouldKeepScrollPosition) { + info(`Running test for ${JSON.stringify(params)}`); + + url = new URL(url); + Object.entries(params).forEach(([k, v]) => { url.searchParams.append(k, v) }); + url.searchParams.append("scrollTo", SCROLL); + + let resetURL = new URL(SJS); + resetURL.search = "?reset"; + return fetch(resetURL).then(() => { + return new Promise((resolve) => { + let count = 0; + window.addEventListener("message", function listener({ data: { commandType, commandData = {} } }) { + if (commandType == "onChangedInputValue") { + let { historyLength, inputValue } = commandData; + + if (shouldAddToHistory) { + is(historyLength, count, "Auto-refresh should add entries to session history"); + } else { + is(historyLength, 1, "Auto-refresh shouldn't add entries to session history"); + } + + is(inputValue, "1234", "Input's value should have been changed"); + + win.postMessage("loadNext", "*"); + return; + } + + is(commandType, "pageShow", "Unknown command type"); + + let { inputValue, scrollPosition } = commandData; + + switch (++count) { + // file_bug1742865.sjs causes 3 loads: + // * first load, returns first meta refresh + // * second load, caused by first meta refresh, returns second meta refresh + // * third load, caused by second meta refresh, doesn't return a meta refresh + case 2: + checkScrollPosition(scrollPosition, shouldKeepScrollPosition); + break; + case 3: + checkScrollPosition(scrollPosition, shouldKeepScrollPosition); + win.postMessage("changeInputValue", "*"); + break; + case 4: + win.postMessage("back", "*"); + break; + case 5: + is(inputValue, "1234", "Entries for auto-refresh should be attached to session history"); + checkScrollPosition(scrollPosition, shouldKeepScrollPosition); + removeEventListener("message", listener); + win.close(); + resolve(); + break; + } + }); + let win = window.open(url); + }); + }); + } + + function doTest(seconds, crossOrigin, shouldAddToHistory, shouldKeepScrollPosition) { + let params = { + seconds, + crossOrigin, + }; + + return openWindowAndCheckRefresh(SJS, params, shouldAddToHistory, shouldKeepScrollPosition).then(() => + openWindowAndCheckRefresh(SJS_OUTER, params, shouldAddToHistory, shouldKeepScrollPosition) + ); + } + + async function runTest() { + const FAST = Math.min(1, REFRESH_REDIRECT_TIMER); + const SLOW = REFRESH_REDIRECT_TIMER + 1; + let tests = [ + // [ time, crossOrigin, shouldAddToHistory, shouldKeepScrollPosition ] + [ FAST, false, false, true ], + [ FAST, true, false, false ], + [ SLOW, false, false, true ], + [ SLOW, true, true, false ], + ]; + + await setup(); + + for (let [ time, crossOrigin, shouldAddToHistory, shouldKeepScrollPosition ] of tests) { + await doTest(time, crossOrigin, shouldAddToHistory, shouldKeepScrollPosition); + } + + SimpleTest.finish(); + } + </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/mochitest/test_bug1743353.html b/docshell/test/mochitest/test_bug1743353.html new file mode 100644 index 0000000000..a5d88df3f6 --- /dev/null +++ b/docshell/test/mochitest/test_bug1743353.html @@ -0,0 +1,57 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test back/forward after pushState</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <script> + SimpleTest.waitForExplicitFinish(); + + function runTest() { + let bc = new BroadcastChannel("bug1743353"); + new Promise((resolve) => { + bc.addEventListener("message", () => { + resolve(); + }, { once: true }); + + window.open("file_bug1743353.html", "", "noopener"); + }).then(() => { + return new Promise(resolve => { + bc.addEventListener("message", () => { + resolve(); + }, { once: true }); + + bc.postMessage("load"); + }) + }).then(() => { + return new Promise(resolve => { + let results = []; + bc.addEventListener("message", function listener({ data }) { + results.push(data); + if (results.length == 3) { + bc.removeEventListener("message", listener); + resolve(results); + } + }); + + bc.postMessage("back"); + }); + }).then((results) => { + is(results[0], "pagehide", "First event should be 'pagehide'."); + is(results[1], "unload", "Second event should be 'unload'."); + is(results[2], "pageshow", "Third event should be 'pageshow'."); + + bc.postMessage("close"); + + SimpleTest.finish(); + }); + } + </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/mochitest/test_bug1747033.html b/docshell/test/mochitest/test_bug1747033.html new file mode 100644 index 0000000000..539b78fec0 --- /dev/null +++ b/docshell/test/mochitest/test_bug1747033.html @@ -0,0 +1,97 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test history after loading multipart</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <script> + SimpleTest.waitForExplicitFinish(); + + function runTest() { + let bc = new BroadcastChannel("bug1747033"); + new Promise(resolve => { + bc.addEventListener("message", ({ data: { historyLength } }) => { + is(historyLength, 1, "Correct length for first normal load."); + + resolve(); + }, { once: true }); + + window.open("file_bug1747033.sjs", "", "noopener"); + }).then(() => { + return new Promise(resolve => { + let loaded = 0; + bc.addEventListener("message", function listener({ data: { historyLength } }) { + ++loaded; + + is(historyLength, 2, `Correct length for multipart load ${loaded}.`); + + // We want 3 parts in total. + if (loaded < 3) { + if (loaded == 2) { + // We've had 2 parts, make the server send the last part. + fetch("file_bug1747033.sjs?sendLastPart"); + } else { + fetch("file_bug1747033.sjs?sendNextPart"); + } + return; + } + + bc.removeEventListener("message", listener); + resolve(); + }); + + bc.postMessage({ cmd: "load", arg: "file_bug1747033.sjs?multipart" }); + }); + }).then(() => { + return new Promise(resolve => { + bc.addEventListener("message", ({ data: { historyLength } }) => { + is(historyLength, 2, "Correct length after calling replaceState in multipart."); + + resolve(); + }, { once: true }); + + bc.postMessage({ cmd: "replaceState", arg: "file_bug1747033.sjs?replaced" }); + }); + }).then(() => { + return new Promise(resolve => { + bc.addEventListener("message", ({ data: { historyLength } }) => { + is(historyLength, 3, "Correct length for first normal load after multipart."); + + resolve(); + }, { once: true }); + + bc.postMessage({ cmd: "load", arg: "file_bug1747033.sjs" }); + }); + }).then(() => { + return new Promise(resolve => { + let goneBack = 0; + bc.addEventListener("message", function listener({ data: { historyLength } }) { + ++goneBack; + + is(historyLength, 3, "Correct length after going back."); + + if (goneBack == 1) { + bc.postMessage({ cmd: "back" }); + } else if (goneBack == 2) { + bc.removeEventListener("message", listener); + resolve(); + } + }); + + bc.postMessage({ cmd: "back" }); + }); + }).then(() => { + bc.postMessage({ cmd: "close" }); + + SimpleTest.finish(); + }); + } + </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/mochitest/test_bug1773192.html b/docshell/test/mochitest/test_bug1773192.html new file mode 100644 index 0000000000..d4c42dc1a7 --- /dev/null +++ b/docshell/test/mochitest/test_bug1773192.html @@ -0,0 +1,61 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test referrer with going back</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <script> + SimpleTest.waitForExplicitFinish(); + + // file_bug1773192_1.html will send a message with some data on pageshow. + function waitForData(bc) { + return new Promise(resolve => { + bc.addEventListener( + "message", + ({ data }) => { + resolve(data); + }, + { once: true } + ); + }); + } + async function runTest() { + let bc = new BroadcastChannel("bug1743353"); + + let getData = waitForData(bc); + + window.open("file_bug1773192_1.html", "", "noreferrer"); + + await getData.then(({ referrer }) => { + is(referrer, "", "Referrer should be empty at first."); + }); + + getData = waitForData(bc); + + // When file_bug1773192_1.html receives this message it will navigate to + // file_bug1773192_2.html. file_bug1773192_2.html removes itself from + // history with replaceState and submits a form with the POST method to + // file_bug1773192_3.sjs. file_bug1773192_3.sjs goes back in history. + // We should end up back at file_bug1773192_1.html, which will send a + // message with some data on pageshow. + bc.postMessage("next"); + + await getData.then(({ location, referrer }) => { + let firstURL = new URL("file_bug1773192_1.html", location).toString(); + is(location, firstURL, "Location should be the first page again."); + is(referrer, firstURL, "Referrer should also be the first page."); + }); + + bc.postMessage("close"); + + SimpleTest.finish(); + } + </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/mochitest/test_bug385434.html b/docshell/test/mochitest/test_bug385434.html new file mode 100644 index 0000000000..8c3ea4a266 --- /dev/null +++ b/docshell/test/mochitest/test_bug385434.html @@ -0,0 +1,211 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=385434 +--> +<head> + <title>Test for Bug 385434</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=385434">Mozilla Bug 385434</a> +<p id="display"></p> +<div id="content"> + <iframe id="frame" style="height:100px; width:100px; border:0"></iframe> + <div id="status" style="display: none"></div> +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 385434 **/ +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("untriaged"); +SimpleTest.expectAssertions(0, 1); // bug 1333702 + +var gNumHashchanges = 0; +var gCallbackOnIframeLoad = false; +var gSampleEvent; + +function statusMsg(msg) { + var msgElem = document.createElement("p"); + msgElem.appendChild(document.createTextNode(msg)); + + document.getElementById("status").appendChild(msgElem); +} + +function longWait() { + setTimeout(function() { gGen.next(); }, 1000); +} + +// onIframeHashchange, onIframeLoad, and onIframeScroll are all called by the +// content we load into our iframe in order to notify the parent frame of an +// event which was fired. +function onIframeHashchange() { + gNumHashchanges++; + gGen.next(); +} + +function onIframeLoad() { + if (gCallbackOnIframeLoad) { + gCallbackOnIframeLoad = false; + gGen.next(); + } +} + +function onIframeScroll() { + is(gNumHashchanges, 0, "onscroll should fire before onhashchange."); +} + +function enableIframeLoadCallback() { + gCallbackOnIframeLoad = true; +} + +function noEventExpected(msg) { + is(gNumHashchanges, 0, msg); + + // Even if there's an error, set gNumHashchanges to 0 so other tests don't + // fail. + gNumHashchanges = 0; +} + +function eventExpected(msg) { + is(gNumHashchanges, 1, msg); + + // Eat up this event, whether the test above was true or not + gNumHashchanges = 0; +} + +/* + * The hashchange event is dispatched asynchronously, so if we want to observe + * it, we have to yield within run_test(), transferring control back to the + * event loop. + * + * When we're expecting our iframe to observe a hashchange event after we poke + * it, we just yield and wait for onIframeHashchange() to call gGen.next() and + * wake us up. + * + * When we're testing to ensure that the iframe doesn't dispatch a hashchange + * event, we try to hook onto the iframe's load event. We call + * enableIframeLoadCallback(), which causes onIframeLoad() to call gGen.next() + * upon the next observed load. After we get our callback, we check that a + * hashchange didn't occur. + * + * We can't always just wait for page load in order to observe that a + * hashchange didn't happen. In these cases, we call longWait() and yield + * until either a hashchange occurs or longWait's callback is scheduled. This + * is something of a hack; it's entirely possible that longWait won't wait long + * enough, and we won't observe what should have been a failure of the test. + * But it shouldn't happen that good code will randomly *fail* this test. + */ +function* run_test() { + /* + * TEST 1 tests that: + * <body onhashchange = ... > works, + * the event is (not) fired at the correct times + */ + var frame = document.getElementById("frame"); + var frameCw = frame.contentWindow; + + enableIframeLoadCallback(); + frameCw.document.location = "file_bug385434_1.html"; + // Wait for the iframe to load and for our callback to fire + yield undefined; + + noEventExpected("No hashchange expected initially."); + + sendMouseEvent({type: "click"}, "link1", frameCw); + yield undefined; + eventExpected("Clicking link1 should trigger a hashchange."); + + sendMouseEvent({type: "click"}, "link1", frameCw); + longWait(); + yield undefined; + // succeed if a hashchange event wasn't triggered while we were waiting + noEventExpected("Clicking link1 again should not trigger a hashchange."); + + sendMouseEvent({type: "click"}, "link2", frameCw); + yield undefined; + eventExpected("Clicking link2 should trigger a hashchange."); + + frameCw.history.go(-1); + yield undefined; + eventExpected("Going back should trigger a hashchange."); + + frameCw.history.go(1); + yield undefined; + eventExpected("Going forward should trigger a hashchange."); + + // window.location has a trailing '#' right now, so we append "link1", not + // "#link1". + frameCw.window.location = frameCw.window.location + "link1"; + yield undefined; + eventExpected("Assigning to window.location should trigger a hashchange."); + + // Set up history in the iframe which looks like: + // file_bug385434_1.html#link1 + // file_bug385434_2.html + // file_bug385434_1.html#foo <-- current page + enableIframeLoadCallback(); + frameCw.window.location = "file_bug385434_2.html"; + yield undefined; + + enableIframeLoadCallback(); + frameCw.window.location = "file_bug385434_1.html#foo"; + yield undefined; + + // Now when we do history.go(-2) on the frame, it *shouldn't* fire a + // hashchange. Although the URIs differ only by their hashes, they belong to + // two different Documents. + frameCw.history.go(-2); + longWait(); + yield undefined; + noEventExpected("Moving between different Documents shouldn't " + + "trigger a hashchange."); + + /* + * TEST 2 tests that: + * <frameset onhashchange = ... > works, + * the event is targeted at the window object + * the event's cancelable, bubbles settings are correct + */ + + enableIframeLoadCallback(); + frameCw.document.location = "file_bug385434_2.html"; + yield undefined; + + frameCw.document.location = "file_bug385434_2.html#foo"; + yield undefined; + + eventExpected("frame onhashchange should fire events."); + // iframe should set gSampleEvent + is(gSampleEvent.target, frameCw, + "The hashchange event should be targeted to the window."); + is(gSampleEvent.type, "hashchange", + "Event type should be 'hashchange'."); + is(gSampleEvent.cancelable, false, + "The hashchange event shouldn't be cancelable."); + is(gSampleEvent.bubbles, false, + "The hashchange event should not bubble."); + + /* + * TEST 3 tests that: + * hashchange is dispatched if the current document readyState is + * not "complete" (bug 504837). + */ + frameCw.document.location = "file_bug385434_3.html"; + yield undefined; + eventExpected("Hashchange should fire even if the document " + + "hasn't finished loading."); + + SimpleTest.finish(); +} + +var gGen = run_test(); +gGen.next(); + +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug387979.html b/docshell/test/mochitest/test_bug387979.html new file mode 100644 index 0000000000..bd92b0ad08 --- /dev/null +++ b/docshell/test/mochitest/test_bug387979.html @@ -0,0 +1,52 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=387979 +--> +<head> + <title>Test for Bug 387979</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=387979">Mozilla Bug 387979</a> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +/** Test for Bug 387979 **/ +function a(s) { + var r; + try { r = frames[0].document.body; } catch (e) { r = e; } + is(r instanceof frames[0].HTMLBodyElement, true, "Can't get body" + s); +} +var p = 0; +function b() { + switch (++p) { + case 1: + frames[0].location = "about:blank"; + break; + case 2: + a("before reload"); + frames[0].location.reload(); + break; + case 3: + a("after reload"); + SimpleTest.finish(); + break; + } +} + +SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +<p id="display"> + <iframe onload="b()"></iframe> + <pre id="p">-</pre> +</p> +</body> +</html> + diff --git a/docshell/test/mochitest/test_bug402210.html b/docshell/test/mochitest/test_bug402210.html new file mode 100644 index 0000000000..326f98cf9f --- /dev/null +++ b/docshell/test/mochitest/test_bug402210.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html> +<!-- +While working on bug 402210, it came up that the code was doing + +a.href = proto + host + +which technically produces "https:host" instead of "https://host" and +that the code was relying on href's setting having fixup behaviour +for this kind of thing. + +If we rely on it, we might as well test for it, even if it isn't the +problem 402210 was meant to fix. + +https://bugzilla.mozilla.org/show_bug.cgi?id=402210 +--> +<head> + <title>Test for Bug 402210</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=402210">Mozilla Bug 402210</a> +<p id="display"> + <a id="testlink">Test Link</a> +</p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +function runTest() { + $("testlink").href = "https:example.com"; + is($("testlink").href, "https://example.com/", "Setting href on an anchor tag should fixup missing slashes after https protocol"); + + $("testlink").href = "ftp:example.com"; + is($("testlink").href, "ftp://example.com/", "Setting href on an anchor tag should fixup missing slashes after non-http protocol"); + + SimpleTest.finish(); +} + +addLoadEvent(runTest); +</script> +</pre> +</body> +</html> + diff --git a/docshell/test/mochitest/test_bug404548.html b/docshell/test/mochitest/test_bug404548.html new file mode 100644 index 0000000000..495341e7b5 --- /dev/null +++ b/docshell/test/mochitest/test_bug404548.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=404548 +--> +<head> + <title>Test for Bug 404548</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=404548">Mozilla Bug 404548</a> +<p id="display"> +</p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +/** Test for Bug 404548 **/ +var firstRemoved = false; +var secondHidden = false; + +SimpleTest.waitForExplicitFinish(); + +var w = window.open("bug404548-subframe.html", "", "width=10,height=10"); + +function finishTest() { + is(firstRemoved, true, "Should have removed iframe from the DOM"); + is(secondHidden, true, "Should have fired pagehide on second kid"); + w.close(); + SimpleTest.finish(); +} +</script> +</pre> +</body> +</html> + diff --git a/docshell/test/mochitest/test_bug413310.html b/docshell/test/mochitest/test_bug413310.html new file mode 100644 index 0000000000..3d3ba56252 --- /dev/null +++ b/docshell/test/mochitest/test_bug413310.html @@ -0,0 +1,106 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=413310 +--> +<head> + <title>Test for Bug 413310</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=413310">Mozilla Bug 413310</a> +<p id="display"> +<script class="testbody" type="text/javascript"> + +if (navigator.platform.startsWith("Mac")) { + SimpleTest.expectAssertions(0, 2); +} else { + SimpleTest.expectAssertions(0, 1); +} + +/** Test for Bug 413310 **/ + +// NOTE: If we ever make subframes do bfcache stuff, this test will need to be +// modified accordingly! It assumes that subframes do NOT get bfcached. +var onloadCount = 0; + +var step = -1; // One increment will come from the initial subframe onload. + // Note that this script should come before the subframe, + // so that doNextStep is defined when its onload handler fires. + +var textContent; + +SimpleTest.waitForExplicitFinish(); + +addLoadEvent(doNextStep); + +function doNextStep() { + ++step; + switch (step) { + case 1: + is(onloadCount, 1, "Loaded initial page"); + is($("i").contentWindow.location.href, + location.href.replace(/test_bug413310.html/, + "bug413310-subframe.html"), + "Unexpected subframe location after initial load"); + $("i").contentDocument.forms[0].submit(); + break; + case 2: + is(onloadCount, 2, "Loaded POST result"); + + is($("i").contentWindow.location.href, + location.href.replace(/test_bug413310.html/, + "bug413310-post.sjs"), + "Unexpected subframe location after POST load"); + + textContent = $("i").contentDocument.body.textContent; + isDeeply(textContent.match(/^POST /), ["POST "], "Not a POST?"); + + $("i").contentWindow.location.hash = "foo"; + setTimeout(doNextStep, 0); + break; + case 3: + is(onloadCount, 2, "Anchor scroll should not fire onload"); + is($("i").contentWindow.location.href, + location.href.replace(/test_bug413310.html/, + "bug413310-post.sjs#foo"), + "Unexpected subframe location after anchor scroll"); + is(textContent, $("i").contentDocument.body.textContent, + "Did a load when scrolling?"); + $("i").contentWindow.location.href = "bug413310-subframe.html"; + break; + case 4: + is(onloadCount, 3, "Done new load"); + is($("i").contentWindow.location.href, + location.href.replace(/test_bug413310.html/, + "bug413310-subframe.html"), + "Unexpected subframe location after new load"); + history.back(); + break; + case 5: + is(onloadCount, 4, + "History traversal didn't fire onload: bfcache issues!"); + is($("i").contentWindow.location.href, + location.href.replace(/test_bug413310.html/, + "bug413310-post.sjs#foo"), + "Unexpected subframe location"); + is(textContent, $("i").contentDocument.body.textContent, + "Did a load when going back?"); + SimpleTest.finish(); + break; + } +} +</script> +<!-- Use a timeout in onload so that we don't do a load immediately inside onload --> +<iframe id="i" src="bug413310-subframe.html" onload="setTimeout(doNextStep, 20)"> +</iframe> +</p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> + diff --git a/docshell/test/mochitest/test_bug475636.html b/docshell/test/mochitest/test_bug475636.html new file mode 100644 index 0000000000..fb1827ad04 --- /dev/null +++ b/docshell/test/mochitest/test_bug475636.html @@ -0,0 +1,52 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=475636 +Test that refresh to data: URIs don't inherit the principal +--> +<head> + <title>Test for Bug 475636</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body onload="gen.next()"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=475636">Mozilla Bug 475636</a> + +<div id="content" style="display: none"> + +</div> +<iframe id=loader></iframe> +<pre id="test"> +<script class="testbody" type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +var gen = runTests(); + +window.private = 42; + +window.addEventListener("message", function(e) { + gen.next(e.data); +}); + +var url = "file_bug475636.sjs?"; + +function* runTests() { + var loader = document.getElementById("loader"); + for (var testNum = 1; ; ++testNum) { + loader.src = url + testNum; + let res = (yield); + if (res == "done") { + SimpleTest.finish(); + return; + } + is(res, "pass"); + } +} + + +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug509055.html b/docshell/test/mochitest/test_bug509055.html new file mode 100644 index 0000000000..d6d871dc99 --- /dev/null +++ b/docshell/test/mochitest/test_bug509055.html @@ -0,0 +1,115 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=509055 +--> +<head> + <title>Test for Bug 509055</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=509055">Mozilla Bug 509055</a> +<p id="display"></p> +<div id="status"></div> +<div id="content"> +</div> +<pre id="test"> + <script type="application/javascript"> + + /** Test for Bug 509055 **/ + + SimpleTest.waitForExplicitFinish(); + + var gGen; + + function shortWait() { + setTimeout(function() { gGen.next(); }, 0, false); + } + + function onChildHashchange(e) { + // gGen might be undefined when we refresh the page, so we have to check here + dump("onChildHashchange() called.\n"); + if (gGen) + gGen.next(); + } + + function onChildLoad(e) { + if (gGen) + gGen.next(); + } + + async function* runTest() { + var popup = window.open("file_bug509055.html", "popup 0", + "height=200,width=200,location=yes," + + "menubar=yes,status=yes,toolbar=yes,dependent=yes"); + popup.hashchangeCallback = onChildHashchange; + popup.onload = onChildLoad; + dump("Waiting for initial load.\n"); + yield undefined; + + // Without this wait, the change to location.hash below doesn't create a + // SHEntry or enable the back button. + shortWait(); + dump("Got initial load. Spinning event loop.\n"); + yield undefined; + + popup.location.hash = "#1"; + dump("Waiting for hashchange.\n"); + yield undefined; + + popup.history.back(); + dump("Waiting for second hashchange.\n"); + yield undefined; // wait for hashchange + + popup.document.title = "Changed"; + + // Wait for listeners to be notified of the title change. + shortWait(); + dump("Got second hashchange. Spinning event loop.\n"); + yield undefined; + + let sheTitle = ""; + if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) { + var sh = SpecialPowers.wrap(popup) + .docShell + .QueryInterface(SpecialPowers.Ci.nsIWebNavigation) + .sessionHistory; + + // Get the title of the inner popup's current SHEntry + sheTitle = sh.legacySHistory.getEntryAtIndex(sh.index).title; + } else { + let chromeScript = SpecialPowers.loadChromeScript(() => { + /* eslint-env mozilla/chrome-script */ + addMessageListener("getTitle", browsingContext => { + // eslint-disable-next-line no-shadow + let sh = browsingContext.sessionHistory; + let title = sh.getEntryAtIndex(sh.index).title; + sendAsyncMessage("title", title); + }); + }); + + let p = chromeScript.promiseOneMessage("title"); + let browsingContext = SpecialPowers.wrap(popup) + .docShell.browsingContext; + chromeScript.sendAsyncMessage("getTitle", browsingContext); + sheTitle = await p; + chromeScript.destroy(); + } + is(sheTitle, "Changed", "SHEntry's title should change when we change."); + + popup.close(); + + SimpleTest.executeSoon(SimpleTest.finish); + } + + window.addEventListener("load", function() { + gGen = runTest(); + gGen.next(); + }); + + </script> + +</body> +</html> diff --git a/docshell/test/mochitest/test_bug511449.html b/docshell/test/mochitest/test_bug511449.html new file mode 100644 index 0000000000..de66eb756d --- /dev/null +++ b/docshell/test/mochitest/test_bug511449.html @@ -0,0 +1,56 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=511449 +--> +<head> + <title>Test for Bug 511449</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <script src="/tests/SimpleTest/NativeKeyCodes.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=511449">Mozilla Bug 511449</a> +<p id="display"></p> +<div id="status"></div> +<div id="content"> +</div> +<input type="text" id="input"> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 511449 **/ + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("untriaged"); +window.addEventListener("load", runTest); + +var win = null; + +function runTest() { + document.getElementById("input").focus(); + win = window.open("file_bug511449.html", ""); + SimpleTest.waitForFocus(runNextTest, win); +} + +function runNextTest() { + var didClose = false; + win.onunload = function() { + didClose = true; + }; + synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_W, {metaKey: 1}, "w", "w"); + + setTimeout(function() { + ok(didClose, "Cmd+W should have closed the tab"); + if (!didClose) { + win.close(); + } + SimpleTest.finish(); + }, 1000); +} + +</script> + +</body> +</html> diff --git a/docshell/test/mochitest/test_bug529119-1.html b/docshell/test/mochitest/test_bug529119-1.html new file mode 100644 index 0000000000..1c89780fc7 --- /dev/null +++ b/docshell/test/mochitest/test_bug529119-1.html @@ -0,0 +1,110 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test bug 529119</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("untriaged"); + +var workingURL = "http://mochi.test:8888/tests/docshell/test/mochitest/bug529119-window.html"; +var faultyURL = "https://www.some-nonexistent-domain-27489274c892748217cn2384.test/"; + +var w = null; +var phase = 0; +var gotWrongPageOnTryAgainClick = false; +// Token that represents which page we currently have loaded. +var token = 0; + +function delay(msec) { + return new Promise(resolve => setTimeout(resolve, msec)); +} + +async function assignToken(tokenToAssign) { + await SpecialPowers.spawn(w, [tokenToAssign], + newToken => { this.content.token = newToken }); +} + +async function pollForPage(win) { + while (true) { + try { + // When we do our navigation, there may be an interstitial about:blank + // page if the navigation involves a process switch. That about:blank + // will exist between the new process's docshell being created and the + // actual page that's being loaded loading (which can happen async from + // the docshell creation). We want to avoid treating the initial + // about:blank as a new page. + // + // We could conceivably expose Document::IsInitialDocument() as a + // ChromeOnly thing and use it here, but let's just filter out all + // about:blank, since we don't expect any in this test. + var haveNewPage = await SpecialPowers.spawn(w, [token], + currentToken => this.content.token != currentToken && + this.content.location.href != "about:blank"); + + if (haveNewPage) { + ++token; + assignToken(token); + break; + } + } catch (e) { + // Something went wrong; just keep waiting. + } + + await delay(100); + } +} + +async function windowLoaded() { + switch (phase) { + case 0: + assignToken(token); + + /* 2. We have succeededfully loaded a page, now go to a faulty URL */ + window.setTimeout(function() { + w.location.href = faultyURL; + }, 0); + + phase = 1; + + await pollForPage(w); + is(await SpecialPowers.spawn(w, [], () => this.content.location.href), + faultyURL, + "Is on an error page initially"); + + /* 3. now, while we are on the error page, try to reload it, actually + click the "Try Again" button */ + SpecialPowers.spawn(w, [], () => this.content.location.reload()); + + await pollForPage(w); + + /* 4-finish, check we are still on the error page */ + is(await SpecialPowers.spawn(w, [], () => this.content.location.href), + faultyURL, + "Is on an error page"); + is(gotWrongPageOnTryAgainClick, false, + "Must not get www.example.com page on reload of an error page"); + w.close(); + SimpleTest.finish(); + break; + + case 1: + /* 4-check, we must not get here! */ + gotWrongPageOnTryAgainClick = true; + break; + } +} + +function startTest() { + /* 1. load a URL that leads to an error page */ + w = window.open(workingURL); +} + +</script> +</head> +<body onload="startTest();"> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug529119-2.html b/docshell/test/mochitest/test_bug529119-2.html new file mode 100644 index 0000000000..a8bd57d4f7 --- /dev/null +++ b/docshell/test/mochitest/test_bug529119-2.html @@ -0,0 +1,116 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test bug 529119</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("untriaged"); + +var workingURL = "http://mochi.test:8888/tests/docshell/test/mochitest/bug529119-window.html"; +// eslint-disable-next-line @microsoft/sdl/no-insecure-url +var faultyURL = "http://some-nonexistent-domain-27489274c892748217cn2384.test/"; + +var w = null; +var phase = 0; +var isWindowLoaded = false; +// Token that represents which page we currently have loaded. +var token = 0; + +function delay(msec) { + return new Promise(resolve => setTimeout(resolve, msec)); +} + +async function assignToken(tokenToAssign) { + await SpecialPowers.spawn(w, [tokenToAssign], + newToken => { this.content.token = newToken }); +} + +// Returns when a new page is loaded and returns whether that page is an +// error page. +async function pollForPage(win) { + while (true) { + try { + // When we do our navigation, there may be an interstitial about:blank + // page if the navigation involves a process switch. That about:blank + // will exist between the new process's docshell being created and the + // actual page that's being loaded loading (which can happen async from + // the docshell creation). We want to avoid treating the initial + // about:blank as a new page. + // + // We could conceivably expose Document::IsInitialDocument() as a + // ChromeOnly thing and use it here, but let's just filter out all + // about:blank, since we don't expect any in this test. + var haveNewPage = await SpecialPowers.spawn(w, [token], + currentToken => this.content.token != currentToken && + this.content.location.href != "about:blank"); + + if (haveNewPage) { + ++token; + assignToken(token); + + // In this test, error pages are non-same-origin with us, and non-error + // pages are same-origin. + let haveErrorPage = false; + try { + win.document.title; + } catch (ex) { + haveErrorPage = true; + } + return haveErrorPage; + } + } catch (e) { + // Something went wrong; just keep waiting. + } + + await delay(100); + } +} + +async function windowLoaded() { + // The code under here should only be run once + // The test popup window workingURL was already opened + if (isWindowLoaded) + return; + isWindowLoaded = true; + + assignToken(token); + + /* 2. We have successfully loaded a page, now go to a faulty URL */ + // XXX The test fails when we change the location synchronously + window.setTimeout(function() { + w.location.href = faultyURL; + }, 0); + + ok(await pollForPage(w), "Waiting for error page succeeded"); + /* 3. now, while we are on the error page, navigate back */ + try { + // We need the SpecialPowers bit, because this is a cross-origin window + // and we normally can't touch .history on those. + await SpecialPowers.spawn(w, [], () => this.content.history.back()); + } catch (ex) { + ok(false, "w.history.back() threw " + ex); + } + + ok(!await pollForPage(w), "Waiting for original page succeeded"); + /* 4-finish, check we are back at the original page */ + is(await SpecialPowers.spawn(w, [], () => this.content.location.href), + workingURL, + "Is on the previous page"); + w.close(); + SimpleTest.finish(); +} + +function startTest() { + /* 1. load a URL that leads to an error page */ + w = window.open(workingURL); +} + +</script> +</head> +<body onload="startTest();"> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug530396.html b/docshell/test/mochitest/test_bug530396.html new file mode 100644 index 0000000000..fa3ddc6db6 --- /dev/null +++ b/docshell/test/mochitest/test_bug530396.html @@ -0,0 +1,56 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=530396 +--> +<head> + <title>Test for Bug 530396</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=530396">Mozilla Bug 530396</a> + +<p> + +<iframe id="testFrame" src="http://mochi.test:8888/tests/docshell/test/mochitest/bug530396-subframe.html"></iframe> + +<pre id="test"> +<script class="testbody" type="text/javascript"> + +// NOTE: If we ever make subframes do bfcache stuff, this test will need to be +// modified accordingly! It assumes that subframes do NOT get bfcached. +var onloadCount = 0; + +var step = 0; + +var gTestFrame = document.getElementById("testFrame"); + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("untriaged"); +addLoadEvent(doNextStep); + +function doNextStep() { + ++step; + switch (step) { + case 1: + is(onloadCount, 1, "Loaded initial page"); + sendMouseEvent({type: "click"}, "target2", gTestFrame.contentWindow); + window.setTimeout(doNextStep, 1000); + break; + + case 2: + is(onloadCount, 1, "opener must be null"); + sendMouseEvent({type: "click"}, "target1", gTestFrame.contentWindow); + break; + + case 3: + is(onloadCount, 2, "don't send referrer with rel=referrer"); + SimpleTest.finish(); + break; + } +} +</script> +</pre> +</html> diff --git a/docshell/test/mochitest/test_bug540462.html b/docshell/test/mochitest/test_bug540462.html new file mode 100644 index 0000000000..6560d604a2 --- /dev/null +++ b/docshell/test/mochitest/test_bug540462.html @@ -0,0 +1,44 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=540462 +--> +<head> + <title>Test for Bug 540462</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=540462">Mozilla Bug 540462</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 540462 **/ + +var win; +function runTest() { + win = window.open("file_bug540462.html", "", "width=100,height=100"); +} + +var dwlCount = 0; +var originalURL; +function documentWriteLoad() { + if (++dwlCount == 1) { + originalURL = win.document.body.firstChild.href; + } else if (dwlCount == 2) { + is(win.document.body.firstChild.href, originalURL, "Wrong href!"); + win.close(); + SimpleTest.finish(); + } +} + +SimpleTest.waitForExplicitFinish(); + +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug551225.html b/docshell/test/mochitest/test_bug551225.html new file mode 100644 index 0000000000..999cb6b945 --- /dev/null +++ b/docshell/test/mochitest/test_bug551225.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=551225 +--> +<head> + <title>Test for Bug 551225</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=551225">Mozilla Bug 551225</a> + +<script type="application/javascript"> + +/** Test for Bug 551225 **/ + +var obj = { + a: new Date("1/1/2000"), + b: /^foo$/, + c: "bar", +}; + +history.replaceState(obj, "", ""); +is(history.state.a.toString(), new Date("1/1/2000").toString(), "Date object."); +is(history.state.b.toString(), "/^foo$/", "Regex"); +is(history.state.c, "bar", "Other state"); + +</script> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug570341.html b/docshell/test/mochitest/test_bug570341.html new file mode 100644 index 0000000000..363f985407 --- /dev/null +++ b/docshell/test/mochitest/test_bug570341.html @@ -0,0 +1,142 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=570341 +--> +<head> + <title>Test for Bug 570341</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +<script> + var start = Date.now(); + var moments = {}; + + var unload = 0; + var wasEnabled = true; + + function collectMoments() { + var win = frames[0]; + var timing = (win.performance && win.performance.timing) || {}; + for (let p in timing) { + moments[p] = timing[p]; + } + for (let p in win) { + if (p.substring(0, 9) == "_testing_") { + moments[p.substring(9)] = win[p]; + } + } + moments.evt_unload = unload; + return moments; + } + + function showSequence(node) { + while (node.firstChild) { + node.firstChild.remove(); + } + var sequence = []; + for (var p in moments) { + sequence.push(p); + } + sequence.sort(function(a, b) { + return moments[a] - moments[b]; + }); + var table = document.createElement("table"); + node.appendChild(table); + var row = document.createElement("tr"); + table.appendChild(row); + var cell = document.createElement("td"); + row.appendChild(cell); + cell.appendChild(document.createTextNode("start")); + cell = document.createElement("td"); + row.appendChild(cell); + cell.appendChild(document.createTextNode(start)); + for (var i = 0; i < sequence.length; ++i) { + var prop = sequence[i]; + row = document.createElement("tr"); + table.appendChild(row); + cell = document.createElement("td"); + row.appendChild(cell); + cell.appendChild(document.createTextNode(prop)); + cell = document.createElement("td"); + row.appendChild(cell); + cell.appendChild(document.createTextNode(moments[prop])); + } + } + + function checkValues() { + var win = frames[0]; + ok(win.performance, + "window.performance is missing or not accessible for frame"); + ok(!win.performance || win.performance.timing, + "window.performance.timing is missing or not accessible for frame"); + collectMoments(); + + var sequences = [ + ["navigationStart", "unloadEventStart", "unloadEventEnd"], + ["navigationStart", "fetchStart", "domainLookupStart", "domainLookupEnd", + "connectStart", "connectEnd", "requestStart", "responseStart", "responseEnd"], + ["responseStart", "domLoading", "domInteractive", "domComplete"], + ["domContentLoadedEventStart", "domContentLoadedEventEnd", + "loadEventStart", "loadEventEnd"], + ]; + + for (var i = 0; i < sequences.length; ++i) { + var seq = sequences[i]; + for (var j = 0; j < seq.length; ++j) { + var prop = seq[j]; + if (j > 0) { + var prevProp = seq[j - 1]; + ok(moments[prevProp] <= moments[prop], + ["Expected ", prevProp, " to happen before ", prop, + ", got ", prevProp, " = ", moments[prevProp], + ", ", prop, " = ", moments[prop]].join("")); + } + } + } + + SimpleTest.finish(); + } + +window.onload = function() { + var win = frames[0]; + win.addEventListener("unload", function() { + unload = Date.now(); + }, true); + var seenLoad = 0; + win.addEventListener("load", function() { + seenLoad = Date.now(); + }, true); + frames[0].location = "bug570341_recordevents.html"; + var interval = setInterval(function() { + // time constants here are arbitrary, chosen to allow the test to pass + var stopPolling = (win.performance && win.performance.loadEventEnd) || + (seenLoad && Date.now() >= seenLoad + 3000) || + Date.now() >= start + 30000; + if (stopPolling) { + clearInterval(interval); + checkValues(); + } else if (win._testing_evt_load) { + seenLoad = Date.now(); + } + }, 100); +}; +</script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=570341">Mozilla Bug 570341</a> +<div id="frames"> +<iframe name="child0" src="navigation/blank.html"></iframe> +</div> +<button type="button" onclick="showSequence(document.getElementById('display'))"> + Show Events</button> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> +SimpleTest.waitForExplicitFinish(); +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug580069.html b/docshell/test/mochitest/test_bug580069.html new file mode 100644 index 0000000000..bb0a3bc823 --- /dev/null +++ b/docshell/test/mochitest/test_bug580069.html @@ -0,0 +1,58 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=580069 +--> +<head> + <title>Test for Bug 580069</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=580069">Mozilla Bug 580069</a> + +<script type="application/javascript"> + +add_task(async function() { + let iframe = document.createElement("iframe"); + iframe.setAttribute("src", "file_bug580069_1.html"); + + // Insert the initial <iframe> document, and wait for page1Load to be called + // after it loads. + document.body.appendChild(iframe); + await new Promise(resolve => { + window.page1Load = resolve; + }); + let iframeCw = iframe.contentWindow; + + info("iframe's location is: " + iframeCw.location + "\n"); + + // Submit the forum and wait for the initial page load using a POST load. + iframeCw.document.getElementById("form").submit(); + let method1 = await new Promise(resolve => { + window.page2Load = resolve; + }); + info("iframe's location is: " + iframeCw.location + ", method is " + method1 + "\n"); + is(method1, "POST", "Method for first load should be POST."); + + // Push a new state, and refresh the page. This refresh shouldn't pop up the + // "are you sure you want to refresh a page with POST data?" dialog. If it + // does, this test will hang and fail, and we'll see 'Refreshing iframe...' at + // the end of the test log. + iframeCw.history.replaceState("", "", "?replaced"); + + info("Refreshing iframe...\n"); + iframeCw.location.reload(); + let method2 = await new Promise(resolve => { + window.page2Load = resolve; + }); + + info("iframe's location is: " + iframeCw.location + ", method is " + method2 + "\n"); + is(method2, "GET", "Method for second load should be GET."); + is(iframeCw.location.search, "?replaced", "Wrong search on iframe after refresh."); +}); +</script> + +</body> +</html> diff --git a/docshell/test/mochitest/test_bug590573.html b/docshell/test/mochitest/test_bug590573.html new file mode 100644 index 0000000000..83554a7a66 --- /dev/null +++ b/docshell/test/mochitest/test_bug590573.html @@ -0,0 +1,198 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=590573 +--> +<head> + <title>Test for Bug 590573</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=590573">Mozilla Bug 590573</a> + +<script type='application/javascript'> +SimpleTest.waitForExplicitFinish(); + +// Listen to the first callback, since this indicates that the page loaded. +var page1LoadCallbackEnabled = true; +function page1Load() { + if (page1LoadCallbackEnabled) { + page1LoadCallbackEnabled = false; + dump("Got page1 load.\n"); + pageLoad(); + } else { + dump("Ignoring page1 load.\n"); + } +} + +var page1PageShowCallbackEnabled = false; +function page1PageShow() { + if (page1PageShowCallbackEnabled) { + page1PageShowCallbackEnabled = false; + dump("Got page1 pageshow.\n"); + pageLoad(); + } else { + dump("Ignoring page1 pageshow.\n"); + } +} + +var page2LoadCallbackEnabled = false; +function page2Load() { + if (page2LoadCallbackEnabled) { + page2LoadCallbackEnabled = false; + dump("Got page2 popstate.\n"); + pageLoad(); + } else { + dump("Ignoring page2 popstate.\n"); + } +} + +var page2PopstateCallbackEnabled = false; +function page2Popstate() { + if (page2PopstateCallbackEnabled) { + page2PopstateCallbackEnabled = false; + dump("Got page2 popstate.\n"); + pageLoad(); + } else { + dump("Ignoring page2 popstate.\n"); + } +} + +var page2PageShowCallbackEnabled = false; +function page2PageShow() { + if (page2PageShowCallbackEnabled) { + page2PageShowCallbackEnabled = false; + dump("Got page2 pageshow.\n"); + pageLoad(); + } else { + dump("Ignoring page2 pageshow.\n"); + } +} + +var popup = window.open("file_bug590573_1.html"); + +var gTestContinuation = null; +var loads = 0; +function pageLoad() { + loads++; + dump("pageLoad(loads=" + loads + ", page location=" + popup.location + ")\n"); + + if (!gTestContinuation) { + gTestContinuation = testBody(); + } + var ret = gTestContinuation.next(); + if (ret.done) { + SimpleTest.finish(); + } +} + +function continueAsync() { + popup.addEventListener("popstate", function() { + popup.requestAnimationFrame(function() { gTestContinuation.next(); }); + }, + {once: true}); +} + +function* testBody() { + is(popup.scrollY, 0, "test 1"); + popup.scroll(0, 100); + + popup.history.pushState("", "", "?pushed"); + is(Math.round(popup.scrollY), 100, "test 2"); + popup.scroll(0, 200); // set state-2's position to 200 + + popup.history.back(); + continueAsync(); + yield; + is(Math.round(popup.scrollY), 100, "test 3"); + popup.scroll(0, 150); // set original page's position to 150 + + popup.history.forward(); + continueAsync(); + yield; + is(Math.round(popup.scrollY), 200, "test 4"); + + popup.history.back(); + continueAsync(); + yield; + is(Math.round(popup.scrollY), 150, "test 5"); + + popup.history.forward(); + continueAsync(); + yield; + is(Math.round(popup.scrollY), 200, "test 6"); + + // At this point, the history looks like: + // PATH POSITION + // file_bug590573_1.html 150 <-- oldest + // file_bug590573_1.html?pushed 200 <-- newest, current + + // Now test that the scroll position is persisted when we have real + // navigations involved. First, we need to spin the event loop so that the + // navigation doesn't replace our current history entry. + + setTimeout(pageLoad, 0); + yield; + + page2LoadCallbackEnabled = true; + popup.location = "file_bug590573_2.html"; + yield; + + ok(popup.location.href.match("file_bug590573_2.html$"), + "Location was " + popup.location + + " but should end with file_bug590573_2.html"); + + is(popup.scrollY, 0, "test 7"); + popup.scroll(0, 300); + + // We need to spin the event loop again before we go back, otherwise the + // scroll positions don't get updated properly. + setTimeout(pageLoad, 0); + yield; + + page1PageShowCallbackEnabled = true; + popup.history.back(); + yield; + + // Spin the event loop again so that we get the right scroll positions. + setTimeout(pageLoad, 0); + yield; + + is(popup.location.search, "?pushed"); + ok(popup.document.getElementById("div1"), "page should have div1."); + + is(Math.round(popup.scrollY), 200, "test 8"); + + popup.history.back(); + continueAsync(); + yield; + is(Math.round(popup.scrollY), 150, "test 9"); + popup.history.forward(); + continueAsync(); + yield; + + is(Math.round(popup.scrollY), 200, "test 10"); + + // Spin one last time... + setTimeout(pageLoad, 0); + yield; + + page2PageShowCallbackEnabled = true; + popup.history.forward(); + yield; + + // Bug 821821, on Android tegras we get 299 instead of 300 sometimes + const scrollY = Math.floor(popup.scrollY); + if (scrollY >= 299 && scrollY <= 300) { + is(1, 1, "test 11"); + } else { + is(1, 0, "test 11, got " + popup.scrollY + " for popup.scrollY instead of 299|300"); + } + popup.close(); +} +</script> + +</body> +</html> diff --git a/docshell/test/mochitest/test_bug598895.html b/docshell/test/mochitest/test_bug598895.html new file mode 100644 index 0000000000..e5589a8c0f --- /dev/null +++ b/docshell/test/mochitest/test_bug598895.html @@ -0,0 +1,52 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=598895 +--> +<head> + <title>Test for Bug 598895</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/WindowSnapshot.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=598895">Mozilla Bug 598895</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 598895 **/ +SimpleTest.waitForExplicitFinish(); + +addLoadEvent(function() { +var win1 = window.open(); +win1.document.body.textContent = "Should show"; + +var windowsLoaded = 0; + +window.onmessage = async function(ev) { + is(ev.data, "loaded", "Message should be 'loaded'"); + if (++windowsLoaded == 2) { + var one = await snapshotWindow(win1); + var two = await snapshotWindow(win2); + var three = await snapshotWindow(win3); + win1.close(); + win2.close(); + win3.close(); + ok(compareSnapshots(one, two, true)[0], "Popups should look identical"); + ok(compareSnapshots(one, three, false)[0], "Popups should not look identical"); + + SimpleTest.finish(); + } +}; + +var win2 = window.open("file_bug598895_1.html"); +var win3 = window.open("file_bug598895_2.html"); +}); +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug634834.html b/docshell/test/mochitest/test_bug634834.html new file mode 100644 index 0000000000..e1f87de000 --- /dev/null +++ b/docshell/test/mochitest/test_bug634834.html @@ -0,0 +1,52 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=634834 +--> +<head> + <title>Test for Bug 634834</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=634834">Mozilla Bug 634834</a> + +<script type='application/javascript'> +SimpleTest.waitForExplicitFinish(); + +function iframe_loaded() { + var loadedAfterPushstate = false; + $("iframe").onload = function() { + loadedAfterPushstate = true; + }; + + var obj = { name: "name" }; + obj.__defineGetter__("a", function() { + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + $("iframe").contentWindow.location = "http://example.com"; + + // Wait until we've loaded example.com. + do { + var r = new XMLHttpRequest(); + r.open("GET", location.href, false); + r.overrideMimeType("text/plain"); + try { r.send(null); } catch (e) {} + } while (!loadedAfterPushstate); + }); + + try { + $("iframe").contentWindow.history.pushState(obj, ""); + ok(false, "pushState should throw exception."); + } catch (e) { + ok(true, "pushState threw an exception."); + } + SimpleTest.finish(); +} + +</script> + +<iframe id='iframe' src='file_bug634834.html' onload='iframe_loaded()'></iframe> + +</body> +</html> diff --git a/docshell/test/mochitest/test_bug637644.html b/docshell/test/mochitest/test_bug637644.html new file mode 100644 index 0000000000..66ed6cb3c8 --- /dev/null +++ b/docshell/test/mochitest/test_bug637644.html @@ -0,0 +1,52 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=637644 +--> +<head> + <title>Test for Bug 637644</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/WindowSnapshot.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=637644">Mozilla Bug 637644</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 637644 **/ +SimpleTest.waitForExplicitFinish(); + +addLoadEvent(function() { +var win1 = window.open("", "", "height=500,width=500"); +win1.document.body.textContent = "Should show"; + +var windowsLoaded = 0; + +window.onmessage = async function(ev) { + is(ev.data, "loaded", "Message should be 'loaded'"); + if (++windowsLoaded == 2) { + var one = await snapshotWindow(win1); + var two = await snapshotWindow(win2); + var three = await snapshotWindow(win3); + win1.close(); + win2.close(); + win3.close(); + ok(compareSnapshots(one, two, true)[0], "Popups should look identical"); + ok(compareSnapshots(one, three, false)[0], "Popups should not look identical"); + + SimpleTest.finish(); + } +}; + +var win2 = window.open("file_bug637644_1.html", "", "height=500,width=500"); +var win3 = window.open("file_bug637644_2.html", "", "height=500,width=500"); +}); +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug640387_1.html b/docshell/test/mochitest/test_bug640387_1.html new file mode 100644 index 0000000000..b8aab054a1 --- /dev/null +++ b/docshell/test/mochitest/test_bug640387_1.html @@ -0,0 +1,107 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=640387 +--> +<head> + <title>Test for Bug 640387</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=640387">Mozilla Bug 640387</a> + +<script type='application/javascript'> +SimpleTest.waitForExplicitFinish(); + +function* test() { + /* Spin the event loop so we get out of the onload handler. */ + SimpleTest.executeSoon(function() { gGen.next(); }); + yield undefined; + + popup.history.pushState("", "", "#hash1"); + popup.history.pushState("", "", "#hash2"); + + // Now the history looks like: + // file_bug640387.html + // file_bug640387.html#hash1 + // file_bug640387.html#hash2 <-- current + + // Going back should trigger a hashchange, which will wake us up from the + // yield. + popup.history.back(); + yield undefined; + ok(true, "Got first hashchange."); + + // Going back should wake us up again. + popup.history.back(); + yield undefined; + ok(true, "Got second hashchange."); + + // Now the history looks like: + // file_bug640387.html <-- current + // file_bug640387.html#hash1 + // file_bug640387.html#hash2 + + // Going forward should trigger a hashchange. + popup.history.forward(); + yield undefined; + ok(true, "Got third hashchange."); + + // Now modify the history so it looks like: + // file_bug640387.html + // file_bug640387.html#hash1 + // file_bug640387.html#hash1 <-- current + popup.history.pushState("", "", "#hash1"); + + // Now when we go back, we should not get a hashchange. Instead, wait for a + // popstate. We need to asynchronously go back because popstate is fired + // sync. + gHashchangeExpected = false; + gCallbackOnPopstate = true; + SimpleTest.executeSoon(function() { popup.history.back(); }); + yield undefined; + ok(true, "Got popstate."); + gCallbackOnPopstate = false; + + // Spin the event loop so hashchange has a chance to fire, if it's going to. + SimpleTest.executeSoon(function() { gGen.next(); }); + yield undefined; + + popup.close(); + SimpleTest.finish(); +} + +var gGen = null; +function childLoad() { + gGen = test(); + gGen.next(); +} + +var gHashchangeExpected = true; +function childHashchange() { + if (gHashchangeExpected) { + gGen.next(); + } else { + ok(false, "Got hashchange when we weren't expecting one."); + } +} + +var gCallbackOnPopstate = false; +function childPopstate() { + if (gCallbackOnPopstate) { + gGen.next(); + } +} + +/* We need to run this test in a popup, because navigating an iframe + * back/forwards tends to cause intermittent orange. */ +var popup = window.open("file_bug640387.html"); + +/* Control now flows up to childLoad(), called once the popup loads. */ + +</script> + +</body> +</html> diff --git a/docshell/test/mochitest/test_bug640387_2.html b/docshell/test/mochitest/test_bug640387_2.html new file mode 100644 index 0000000000..c248a64836 --- /dev/null +++ b/docshell/test/mochitest/test_bug640387_2.html @@ -0,0 +1,89 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=640387 +--> +<head> + <title>Test for Bug 640387</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=640387">Mozilla Bug 640387</a> + +<!-- Test that, when going from + + http://example.com/#foo + +to + + http://example.com/ + +via a non-history load, we do a true load, rather than a scroll. --> + +<script type='application/javascript'> +SimpleTest.waitForExplicitFinish(); + +var callbackOnLoad = false; +function childLoad() { + if (callbackOnLoad) { + callbackOnLoad = false; + gGen.next(); + } +} + +var errorOnHashchange = false; +var callbackOnHashchange = false; +function childHashchange() { + if (errorOnHashchange) { + ok(false, "Got unexpected hashchange."); + } + if (callbackOnHashchange) { + callbackOnHashchange = false; + gGen.next(); + } +} + +function* run_test() { + var iframe = $("iframe").contentWindow; + + ok(true, "Got first load"); + + // Spin the event loop so we exit the onload handler. + SimpleTest.executeSoon(function() { gGen.next(); }); + yield undefined; + + let origLocation = iframe.location + ""; + callbackOnHashchange = true; + iframe.location.hash = "#1"; + // Wait for a hashchange event. + yield undefined; + + ok(true, "Got hashchange."); + + iframe.location = origLocation; + // This should produce a load event and *not* a hashchange, because the + // result of the load is a different document than we had previously. + callbackOnLoad = true; + errorOnHashchange = true; + yield undefined; + + ok(true, "Got final load."); + + // Spin the event loop to give hashchange a chance to fire, if it's going to. + SimpleTest.executeSoon(function() { gGen.next(); }); + yield undefined; + + SimpleTest.finish(); +} + +callbackOnLoad = true; +var gGen = run_test(); + +</script> + +<iframe id='iframe' src='file_bug640387.html'></iframe> + +</body> +</html> diff --git a/docshell/test/mochitest/test_bug653741.html b/docshell/test/mochitest/test_bug653741.html new file mode 100644 index 0000000000..ca29c5ef08 --- /dev/null +++ b/docshell/test/mochitest/test_bug653741.html @@ -0,0 +1,49 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=653741 +--> +<head> + <title>Test for Bug 653741</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/WindowSnapshot.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=653741">Mozilla Bug 653741</a> + +<script type="application/javascript"> + +/** Test for Bug 653741 **/ +SimpleTest.waitForExplicitFinish(); + +function childLoad() { + // Spin the event loop so we leave the onload handler. + SimpleTest.executeSoon(childLoad2); +} + +function childLoad2() { + let cw = $("iframe").contentWindow; + + // Save the Y offset. For sanity's sake, make sure it's not 0, because we + // should be at the bottom of the page! + let origYOffset = Math.round(cw.pageYOffset); + ok(origYOffset != 0, "Original Y offset is not 0."); + + // Scroll the iframe to the top, then navigate to #bottom again. + cw.scrollTo(0, 0); + + // Our current location is #bottom, so this should scroll us down to the + // bottom again. + cw.location = cw.location + ""; + + is(Math.round(cw.pageYOffset), origYOffset, "Correct offset after reloading page."); + SimpleTest.finish(); +} + +</script> + +<iframe height='100px' id='iframe' src='file_bug653741.html#bottom'></iframe> + +</body> +</html> diff --git a/docshell/test/mochitest/test_bug660404.html b/docshell/test/mochitest/test_bug660404.html new file mode 100644 index 0000000000..459035d4a5 --- /dev/null +++ b/docshell/test/mochitest/test_bug660404.html @@ -0,0 +1,76 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=660404 +--> +<head> + <title>Test for Bug 660404</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=660404">Mozilla Bug 660404</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 660404 **/ +SimpleTest.waitForExplicitFinish(); + +var textContent = +` + var bc = new BroadcastChannel("bug660404_multipart"); + bc.postMessage({command: "finishTest", + textContent: window.document.documentElement.textContent, + innerHTML: window.document.documentElement.innerHTML + }); + bc.close(); + window.close(); +`; +var innerHTML = +`<head><script> + var bc = new BroadcastChannel("bug660404_multipart"); + bc.postMessage({command: "finishTest", + textContent: window.document.documentElement.textContent, + innerHTML: window.document.documentElement.innerHTML + }); + bc.close(); + window.close(); +</` +// eslint-disable-next-line no-useless-concat ++ `script></head>` +; +var bc_multipart = new BroadcastChannel("bug660404_multipart"); +bc_multipart.onmessage = (msgEvent) => { + var msg = msgEvent.data; + var command = msg.command; + if (command == "finishTest") { + is(msg.textContent, textContent); + is(msg.innerHTML, innerHTML); + bc_multipart.close(); + SimpleTest.finish(); + } +} +var bc = new BroadcastChannel("bug660404"); +bc.onmessage = (msgEvent) => { + var msg = msgEvent.data; + var command = msg.command; + if (command == "pagehide") { + is(msg.persisted, true, "Should be bfcached when navigating to multipart"); + bc.close(); + } +} + +// If Fission is disabled, the pref is no-op. +SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => { + // Have to open a new window, since there's no bfcache in subframes + window.open("file_bug660404-1.html", "", "noopener"); +}); + +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug662170.html b/docshell/test/mochitest/test_bug662170.html new file mode 100644 index 0000000000..fa0f3b6d4c --- /dev/null +++ b/docshell/test/mochitest/test_bug662170.html @@ -0,0 +1,51 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=662170 +--> +<head> + <title>Test for Bug 662170</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/WindowSnapshot.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=662170">Mozilla Bug 662170</a> + +<script type="application/javascript"> + +/** Test for Bug 662170 **/ +SimpleTest.waitForExplicitFinish(); + +function childLoad() { + // Spin the event loop so we leave the onload handler. + SimpleTest.executeSoon(childLoad2); +} + +function childLoad2() { + let cw = $("iframe").contentWindow; + + // When we initially load the page, we should be at the top. + is(cw.pageYOffset, 0, "Initial Y offset should be 0."); + + // Scroll the iframe to the bottom. + cw.scrollTo(0, 300); + + // Did we actually scroll somewhere? + isnot(Math.round(cw.pageYOffset), 0, "Y offset should be non-zero after scrolling."); + + // Now load file_bug662170.html#, which should take us to the top of the + // page. + cw.location = cw.location + "#"; + + is(cw.pageYOffset, 0, "Correct Y offset after loading #."); + SimpleTest.finish(); +} + +</script> + +<!-- When the iframe loads, it calls childLoad(). --> +<iframe height='100px' id='iframe' src='file_bug662170.html'></iframe> + +</body> +</html> diff --git a/docshell/test/mochitest/test_bug668513.html b/docshell/test/mochitest/test_bug668513.html new file mode 100644 index 0000000000..09c848b6c1 --- /dev/null +++ b/docshell/test/mochitest/test_bug668513.html @@ -0,0 +1,28 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=668513 +--> +<head> + <title>Test for Bug 668513</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=668513">Mozilla Bug 668513</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> +if (navigator.platform.startsWith("Linux")) { + SimpleTest.expectAssertions(0, 1); +} + +SimpleTest.waitForExplicitFinish(); +window.open("file_bug668513.html"); +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug669671.html b/docshell/test/mochitest/test_bug669671.html new file mode 100644 index 0000000000..c45f4ea89b --- /dev/null +++ b/docshell/test/mochitest/test_bug669671.html @@ -0,0 +1,145 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=669671 +--> +<head> + <title>Test for Bug 669671</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=669671">Mozilla Bug 669671</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** + * Test for Bug 669671. + * + * This is a bit complicated. We have a script, file_bug669671.sjs, which counts + * how many times it's loaded and returns that count in the body of an HTML + * document. For brevity, call this page X. + * + * X is sent with Cache-Control: max-age=0 and can't be bfcached (it has an + * onunload handler). Our test does the following in a popup: + * + * 1) Load X?pushed, to prime the cache. + * 2) Navigate to X. + * 3) Call pushState and navigate from X to X?pushed. + * 4) Navigate to X?navigated. + * 5) Go back (to X?pushed). + * + * We do all this work so we can check that in step 5, we fetch X?pushed from + * the network -- we shouldn't use our cached copy, because of the + * cache-control header X sends. + * + * Then we go back and repeat the whole process but call history.replaceState + * instead of pushState. And for good measure, we test once more, this time + * modifying only the hash of the URI using replaceState. In this case, we + * *should* load from the cache. + * + **/ +SimpleTest.requestLongerTimeout(2); +SimpleTest.waitForExplicitFinish(); + +function onChildLoad() { + SimpleTest.executeSoon(function() { gGen.next(); }); +} + +var _loadCount = 0; +function checkPopupLoadCount() { + is(popup.document.body.innerHTML, _loadCount + "", "Load count"); + + // We normally want to increment _loadCount here. But if the test fails + // because we didn't do a load we should have, let's not cause a cascade of + // failures by incrementing _loadCount. + var origCount = _loadCount; + if (popup.document.body.innerHTML >= _loadCount + "") + _loadCount++; + return origCount; +} + +function* test() { + // Step 0 - Make sure the count is reset to 0 in case of reload + popup.location = "file_bug669671.sjs?countreset"; + yield; + is(popup.document.body.innerHTML, "0", + "Load count should be reset to 0"); + + // Step 1 - The popup's body counts how many times we've requested the + // resource. This is the first time we've requested it, so it should be '0'. + checkPopupLoadCount(); + + // Step 2 - We'll get another onChildLoad when this finishes. + popup.location = "file_bug669671.sjs"; + yield undefined; + + // Step 3 - Call pushState and change the URI back to ?pushed. + checkPopupLoadCount(); + popup.history.pushState("", "", "?pushed"); + + // Step 4 - Navigate away. This should trigger another onChildLoad. + popup.location = "file_bug669671.sjs?navigated-1"; + yield undefined; + + // Step 5 - Go back. This should result in another onload (because the file is + // not in bfcache) and should be the fourth time we've requested the sjs file. + checkPopupLoadCount(); + popup.history.back(); + yield undefined; + + // This is the check which was failing before we fixed the bug. + checkPopupLoadCount(); + + popup.close(); + + // Do the whole thing again, but with replaceState. + popup = window.open("file_bug669671.sjs?replaced"); + yield undefined; + checkPopupLoadCount(); + popup.location = "file_bug669671.sjs"; + yield undefined; + checkPopupLoadCount(); + popup.history.replaceState("", "", "?replaced"); + popup.location = "file_bug669671.sjs?navigated-2"; + yield undefined; + checkPopupLoadCount(); + popup.history.back(); + yield undefined; + checkPopupLoadCount(); + popup.close(); + + // Once more, with feeling. Notice that we don't have to prime the cache + // with an extra load here, because X and X#hash share the same cache entry. + popup = window.open("file_bug669671.sjs?hash-test"); + yield undefined; + var initialCount = checkPopupLoadCount(); + popup.history.replaceState("", "", "#hash"); + popup.location = "file_bug669671.sjs?navigated-3"; + yield undefined; + checkPopupLoadCount(); + popup.history.back(); + yield undefined; + is(popup.document.body.innerHTML, initialCount + "", + "Load count (should be cached)"); + popup.close(); + + SimpleTest.finish(); +} + +var gGen = test(); +var popup; + +// Disable RCWN to make cache behavior deterministic. +SpecialPowers.pushPrefEnv({set: [["network.http.rcwn.enabled", false]]}, () => { + // This will call into onChildLoad once it loads. + popup = window.open("file_bug669671.sjs?pushed"); +}); +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug675587.html b/docshell/test/mochitest/test_bug675587.html new file mode 100644 index 0000000000..e6bd71a47f --- /dev/null +++ b/docshell/test/mochitest/test_bug675587.html @@ -0,0 +1,33 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=675587 +--> +<head> + <title>Test for Bug 675587</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=675587">Mozilla Bug 675587</a> +<p id="display"> + <iframe src="file_bug675587.html#hash"></iframe> +</p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 675587 **/ +SimpleTest.waitForExplicitFinish(); +addLoadEvent(function() { + ok(window.frames[0].location.href.endsWith("file_bug675587.html#"), + "Should have the right href"); + SimpleTest.finish(); +}); + +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug680257.html b/docshell/test/mochitest/test_bug680257.html new file mode 100644 index 0000000000..4d5736ac0a --- /dev/null +++ b/docshell/test/mochitest/test_bug680257.html @@ -0,0 +1,76 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=680257 +--> +<head> + <title>Test for Bug 680257</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=680257">Mozilla Bug 680257</a> + +<script type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +var popup = window.open("file_bug680257.html"); + +var gTestContinuation = null; +function continueAsync() { + popup.addEventListener("hashchange", + function(e) { gTestContinuation.next(); }, { once: true }); +} + +// The popup will call into popupLoaded() once it loads. +function popupLoaded() { + // runTests() needs to be called from outside popupLoaded's onload handler. + // Otherwise, the navigations we do in runTests won't create new SHEntries. + SimpleTest.executeSoon(function() { + if (!gTestContinuation) { + gTestContinuation = runTests(); + } + gTestContinuation.next(); + }); +} + +function* runTests() { + checkPopupLinkStyle(false, "Initial"); + + popup.location.hash = "a"; + continueAsync(); + yield; + checkPopupLinkStyle(true, "After setting hash"); + + popup.history.back(); + continueAsync(); + yield; + + checkPopupLinkStyle(false, "After going back"); + + popup.history.forward(); + continueAsync(); + yield; + checkPopupLinkStyle(true, "After going forward"); + + popup.close(); + SimpleTest.finish(); +} + +function checkPopupLinkStyle(isTarget, desc) { + var link = popup.document.getElementById("a"); + var style = popup.getComputedStyle(link); + var color = style.getPropertyValue("color"); + + // Color is red if isTarget, black otherwise. + if (isTarget) { + is(color, "rgb(255, 0, 0)", desc); + } else { + is(color, "rgb(0, 0, 0)", desc); + } +} + +</script> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug691547.html b/docshell/test/mochitest/test_bug691547.html new file mode 100644 index 0000000000..706cd5013b --- /dev/null +++ b/docshell/test/mochitest/test_bug691547.html @@ -0,0 +1,59 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=691547 +--> +<head> + <title>Test for Bug 691547</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + var navStart = 0; + var beforeReload = 0; + function onContentLoad() { + var frame = frames[0]; + if (!navStart) { + // First time we perform navigation in subframe. The bug is that + // load in subframe causes timing.navigationStart to be recorded + // as if it was a start of the next navigation. + var innerFrame = frame.frames[0]; + navStart = frame.performance.timing.navigationStart; + innerFrame.location = "bug570341_recordevents.html"; + // Let's wait a bit so the difference is clear anough. + setTimeout(reload, 3000); + } else { + // Content reloaded, time to check. We are allowing a huge time slack, + // in case clock is imprecise. If we have a bug, the difference is + // expected to be about the timeout value set above. + var diff = frame.performance.timing.navigationStart - beforeReload; + ok(diff >= -200, + "navigationStart should be set after reload request. " + + "Measured difference: " + diff + " (should be positive)"); + SimpleTest.finish(); + } + } + function reload() { + var frame = frames[0]; + ok(navStart == frame.performance.timing.navigationStart, + "navigationStart should not change when frame loads."); + beforeReload = Date.now(); + frame.location.reload(); + } + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=570341">Mozilla Bug 570341</a> +<div id="frames"> +<iframe name="frame0" id="frame0" src="bug691547_frame.html" onload="onContentLoad()"></iframe> +</div> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("untriaged"); +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug694612.html b/docshell/test/mochitest/test_bug694612.html new file mode 100644 index 0000000000..445d0433d8 --- /dev/null +++ b/docshell/test/mochitest/test_bug694612.html @@ -0,0 +1,34 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=694612 +--> +<head> + <title>Test for Bug 694612</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=694612">Mozilla Bug 694612</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> +/** Test for Bug 694612 **/ +SimpleTest.waitForExplicitFinish(); + +window.addEventListener("message", receiveMessage); +function receiveMessage(event) { + ok(event.data.result, "should have performance API in an <object>"); + window.removeEventListener("message", receiveMessage); + SimpleTest.finish(); +} +</script> +<object type="text/html" + data="data:text/html,<script>parent.postMessage({result:performance!=null},'*');</script>"> +</object> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug703855.html b/docshell/test/mochitest/test_bug703855.html new file mode 100644 index 0000000000..8e3d9b5a04 --- /dev/null +++ b/docshell/test/mochitest/test_bug703855.html @@ -0,0 +1,79 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=703855 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 703855</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=703855">Mozilla Bug 703855</a> +<p id="display"></p> +<div id="content" style="display: none"> + <iframe id="f" src="file_bug703855.html"></iframe> +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 703855 **/ + +SimpleTest.waitForExplicitFinish(); + +var timingAttributes = [ + "connectEnd", + "connectStart", + "domComplete", + "domContentLoadedEventEnd", + "domContentLoadedEventStart", + "domInteractive", + "domLoading", + "domainLookupEnd", + "domainLookupStart", + "fetchStart", + "loadEventEnd", + "loadEventStart", + "navigationStart", + "redirectEnd", + "redirectStart", + "requestStart", + "responseEnd", + "responseStart", + "unloadEventEnd", + "unloadEventStart", +]; +var originalTiming = {}; + +function runTest() { + var timing = $("f").contentWindow.performance.timing; + for (let i in timingAttributes) { + originalTiming[timingAttributes[i]] = timing[timingAttributes[i]]; + } + + var doc = $("f").contentDocument; + doc.open(); + doc.write("<!DOCTYPE html>"); + doc.close(); + + SimpleTest.executeSoon(function() { + var newTiming = $("f").contentWindow.performance.timing; + for (let i in timingAttributes) { + is(newTiming[timingAttributes[i]], originalTiming[timingAttributes[i]], + "document.open should not affect value of " + timingAttributes[i]); + } + SimpleTest.finish(); + }); +} + +addLoadEvent(function() { + SimpleTest.executeSoon(runTest); +}); + + + +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug728939.html b/docshell/test/mochitest/test_bug728939.html new file mode 100644 index 0000000000..168184099a --- /dev/null +++ b/docshell/test/mochitest/test_bug728939.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=728939 +--> +<head> + <title>Test for Bug 728939</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=728939">Mozilla Bug 728939</a> + +<script type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +// Called when the popup finishes loading. +function popupLoaded() { + popup.location.hash = "#foo"; + is(popup.document.URL, popup.location.href, "After hashchange."); + + popup.history.pushState("", "", "bar"); + is(popup.document.URL, popup.location.href, "After pushState."); + + popup.history.replaceState("", "", "baz"); + is(popup.document.URL, popup.location.href, "After replaceState."); + + popup.close(); + SimpleTest.finish(); +} + +var popup = window.open("file_bug728939.html"); + +</script> +</body> +</html> diff --git a/docshell/test/mochitest/test_bug797909.html b/docshell/test/mochitest/test_bug797909.html new file mode 100644 index 0000000000..75e91d9052 --- /dev/null +++ b/docshell/test/mochitest/test_bug797909.html @@ -0,0 +1,66 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=797909 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 797909</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=797909">Mozilla Bug 797909</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + /** Test for Bug 797909 **/ + + SimpleTest.waitForExplicitFinish(); + + function runTest() { + var iframe = document.getElementById("ifr"); + try { + iframe.contentWindow.document; + ok(false, "Should have thrown an exception"); + } catch (ex) { + ok(true, "Got an exception"); + } + + iframe = document.createElement("iframe"); + // set sandbox attribute + iframe.sandbox = "allow-scripts"; + // and then insert into the doc + document.body.appendChild(iframe); + + try { + iframe.contentWindow.document; + ok(false, "Should have thrown an exception"); + } catch (ex) { + ok(true, "Got an exception"); + } + + iframe = document.createElement("iframe"); + // set sandbox attribute + iframe.sandbox = "allow-same-origin"; + // and then insert into the doc + document.body.appendChild(iframe); + + try { + iframe.contentWindow.document; + ok(true, "Shouldn't have thrown an exception"); + } catch (ex) { + ok(false, "Got an unexpected exception"); + } + + SimpleTest.finish(); + } + +</script> +</pre> +<iframe id="ifr" sandbox = "allow-scripts"></iframe> +</body> +</html> diff --git a/docshell/test/mochitest/test_close_onpagehide_by_history_back.html b/docshell/test/mochitest/test_close_onpagehide_by_history_back.html new file mode 100644 index 0000000000..33140502f7 --- /dev/null +++ b/docshell/test/mochitest/test_close_onpagehide_by_history_back.html @@ -0,0 +1,24 @@ +<!doctype html> +<title>Test for closing window in pagehide event callback caused by history.back()</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1432396">Mozilla Bug 1432396</a> +<p id="display"></p> +<script> +SimpleTest.waitForExplicitFinish(); + +const w = window.open("file_close_onpagehide1.html"); +window.addEventListener("message", e => { + is(e.data, "initial", "The initial page loaded"); + window.addEventListener("message", evt => { + is(evt.data, "second", "The second page loaded"); + w.onpagehide = () => { + w.close(); + info("try to close the popped up window in onpagehide"); + SimpleTest.finish(); + }; + w.history.back(); + }, { once: true }); + w.location = "file_close_onpagehide2.html"; +}, { once: true }); +</script> diff --git a/docshell/test/mochitest/test_close_onpagehide_by_window_close.html b/docshell/test/mochitest/test_close_onpagehide_by_window_close.html new file mode 100644 index 0000000000..8b094cdaa4 --- /dev/null +++ b/docshell/test/mochitest/test_close_onpagehide_by_window_close.html @@ -0,0 +1,20 @@ +<!doctype html> +<title>Test for closing window in pagehide event callback caused by window.close()</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1432396">Mozilla Bug 1432396</a> +<p id="display"></p> +<script> +SimpleTest.waitForExplicitFinish(); + +const w = window.open("file_close_onpagehide1.html"); +window.addEventListener("message", e => { + is(e.data, "initial", "The initial page loaded"); + w.onpagehide = () => { + w.close(); + info("try to close the popped up window in onpagehide"); + SimpleTest.finish(); + }; + w.close(); +}, { once: true }); +</script> diff --git a/docshell/test/mochitest/test_compressed_multipart.html b/docshell/test/mochitest/test_compressed_multipart.html new file mode 100644 index 0000000000..ff182d33fc --- /dev/null +++ b/docshell/test/mochitest/test_compressed_multipart.html @@ -0,0 +1,41 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1600211 + +Loads a document that is served as multipart/x-mixed-replace as well as gzip compressed. +Checks that we correctly decompress and display it (via running JS within the document to notify us). +--> +<head> + <title>Test for Bug 1600211</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=1600211">Mozilla Bug 1600211</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 1600211 **/ +SimpleTest.waitForExplicitFinish(); + +var w; + +function finishTest() { + is(w.document.documentElement.textContent, "opener.finishTest();"); + is(w.document.documentElement.innerHTML, "<head><script>opener.finishTest();</" + + "script></head>"); + w.close(); + SimpleTest.finish(); +} + +w = window.open("file_compressed_multipart"); + +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_content_javascript_loads.html b/docshell/test/mochitest/test_content_javascript_loads.html new file mode 100644 index 0000000000..eabc1d314e --- /dev/null +++ b/docshell/test/mochitest/test_content_javascript_loads.html @@ -0,0 +1,163 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test for Bug 1647519</title> + <meta charset="utf-8"> + <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=1647519">Mozilla Bug 1647519</a> + +<script type="application/javascript"> +"use strict"; + +function promiseMessage(source, filter = event => true) { + return new Promise(resolve => { + function listener(event) { + if (event.source == source && filter(event)) { + window.removeEventListener("message", listener); + resolve(event); + } + } + window.addEventListener("message", listener); + }); +} + +async function runTests(resourcePath) { + /* globals Assert, content */ + let doc = content.document; + + // Sends a message to the given target window and waits for a response a few + // times to (more or less) ensure that a `javascript:` load request has had + // time to succeed, if it were going to. + async function doSomeRoundTrips(target) { + for (let i = 0; i < 3; i++) { + // Note: The ping message needs to be sent from a script running in the + // content scope or there will be no source window for the reply to be + // sent to. + await content.wrappedJSObject.ping(target); + } + } + + function promiseEvent(target, name) { + return new Promise(resolve => { + target.addEventListener(name, resolve, { once: true }); + }); + } + + function createIframe(host, id) { + let iframe = doc.createElement("iframe"); + iframe.id = id; + iframe.name = id; + iframe.src = `https://${host}${resourcePath}file_content_javascript_loads_frame.html`; + doc.body.appendChild(iframe); + return promiseEvent(iframe, "load"); + } + + const ID_SAME_ORIGIN = "frame-same-origin"; + const ID_SAME_BASE_DOMAIN = "frame-same-base-domain"; + const ID_CROSS_BASE_DOMAIN = "frame-cross-base-domain"; + + await Promise.all([ + createIframe("example.com", ID_SAME_ORIGIN), + createIframe("test1.example.com", ID_SAME_BASE_DOMAIN), + createIframe("example.org", ID_CROSS_BASE_DOMAIN), + ]); + + let gotJSLoadFrom = null; + let pendingJSLoadID = null; + content.addEventListener("message", event => { + if ("javascriptLoadID" in event.data) { + Assert.equal( + event.data.javascriptLoadID, + pendingJSLoadID, + "Message from javascript: load should have the expected ID" + ); + Assert.equal( + gotJSLoadFrom, + null, + "Should not have seen a previous load message this cycle" + ); + gotJSLoadFrom = event.source.name; + } + }); + + async function watchForJSLoads(frameName, expected, task) { + let loadId = Math.random(); + + let jsURI = + "javascript:" + + encodeURI(`parent.postMessage({ javascriptLoadID: ${loadId} }, "*")`); + + pendingJSLoadID = loadId; + gotJSLoadFrom = null; + + await task(jsURI); + + await doSomeRoundTrips(content.wrappedJSObject[frameName]); + + if (expected) { + Assert.equal( + gotJSLoadFrom, + frameName, + `Should have seen javascript: URI loaded into ${frameName}` + ); + } else { + Assert.equal( + gotJSLoadFrom, + null, + "Should not have seen javascript: URI loaded" + ); + } + } + + let frames = [ + { name: ID_SAME_ORIGIN, expectLoad: true }, + { name: ID_SAME_BASE_DOMAIN, expectLoad: false }, + { name: ID_CROSS_BASE_DOMAIN, expectLoad: false }, + ]; + for (let { name, expectLoad } of frames) { + info(`Checking loads for frame "${name}". Expecting loads: ${expectLoad}`); + + info("Checking location setter"); + await watchForJSLoads(name, expectLoad, jsURI => { + // Note: We need to do this from the content scope since security checks + // depend on the JS caller scope. + content.wrappedJSObject.setFrameLocation(name, jsURI); + }); + + info("Checking targeted <a> load"); + await watchForJSLoads(name, expectLoad, jsURI => { + let a = doc.createElement("a"); + a.target = name; + a.href = jsURI; + doc.body.appendChild(a); + a.click(); + a.remove(); + }); + + info("Checking targeted window.open load"); + await watchForJSLoads(name, expectLoad, jsURI => { + content.wrappedJSObject.open(jsURI, name); + }); + } +} + +add_task(async function() { + const resourcePath = location.pathname.replace(/[^\/]+$/, ""); + + let win = window.open( + `https://example.com${resourcePath}file_content_javascript_loads_root.html` + ); + await promiseMessage(win, event => event.data == "ready"); + + await SpecialPowers.spawn(win, [resourcePath], runTests); + + win.close(); +}); +</script> + +</body> +</html> diff --git a/docshell/test/mochitest/test_double_submit.html b/docshell/test/mochitest/test_double_submit.html new file mode 100644 index 0000000000..640930718d --- /dev/null +++ b/docshell/test/mochitest/test_double_submit.html @@ -0,0 +1,98 @@ +<!doctype html> +<html> + <head> + <title>Test for Bug 1590762</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> + <iframe name="targetFrame" id="targetFrame"></iframe> + <form id="form" action="double_submit.sjs?delay=1000" method="POST" target="targetFrame"> + <input id="token" type="text" name="token" value=""> + <input id="button" type="submit"> + </form> + <script> + "use strict"; + + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + const CROSS_ORIGIN_URI = "http://test1.example.com/tests/docshell/test/mochitest/ping.html"; + + function asyncClick(counts) { + let frame = document.createElement('iframe'); + frame.addEventListener( + 'load', () => frame.contentWindow.postMessage({command: "start"}, "*"), + { once:true }); + frame.src = "clicker.html"; + + addEventListener('message', ({source}) => { + if (source === frame.contentWindow) { + counts.click++; + synthesizeMouse(document.getElementById('button'), 5, 5, {}); + } + }, { once: true }); + + document.body.appendChild(frame); + return stop; + } + + function click(button) { + synthesizeMouse(button, 5, 5, {}); + } + + add_task(async function runTest() { + let frame = document.getElementById('targetFrame'); + await new Promise(resolve => { + addEventListener('message', resolve, {once: true}); + frame.src = CROSS_ORIGIN_URI; + }); + + let form = document.getElementById('form'); + let button = document.getElementById('button'); + + let token = document.getElementById('token'); + token.value = "first"; + + await new Promise((resolve, reject) => { + let counts = { click: 0, submit: 0 }; + form.addEventListener('submit', () => counts.submit++); + asyncClick(counts); + form.requestSubmit(button); + token.value = "bad"; + let steps = { + good: { + entered: false, + next: () => { steps.good.entered = true; resolve(); }, + assertion: () => { + ok(steps.first.entered && !steps.bad.entered, "good comes after first, but not bad") + } + }, + first: { + entered: false, + next: () => { steps.first.entered = true; token.value = "good"; click(button); }, + assertion: () => { + ok(!steps.good.entered && !steps.bad.entered, "first message is first") + is(counts.click, 1, "clicked"); + is(counts.submit, 2, "did submit"); + } + }, + bad: { + entered: false, + next: () => { reject(); }, + assertion: () => ok(false, "we got a bad message") + } + }; + addEventListener('message', ({source, data}) => { + if (source !== frame.contentWindow) { + return; + } + + let step = steps[data] || reject; + step.assertion(); + step.next(); + }) + }); + }); + </script> + </body> +</html> diff --git a/docshell/test/mochitest/test_forceinheritprincipal_overrule_owner.html b/docshell/test/mochitest/test_forceinheritprincipal_overrule_owner.html new file mode 100644 index 0000000000..70d610a677 --- /dev/null +++ b/docshell/test/mochitest/test_forceinheritprincipal_overrule_owner.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + +<script type="text/javascript"> + +var channel = SpecialPowers.wrap(window).docShell.currentDocumentChannel; +var loadInfo = channel.loadInfo; + +// 1) perform some sanity checks +var triggeringPrincipal = channel.loadInfo.triggeringPrincipal.asciiSpec; +var loadingPrincipal = channel.loadInfo.loadingPrincipal.asciiSpec; +var principalToInherit = channel.loadInfo.principalToInherit.asciiSpec; + +ok(triggeringPrincipal.startsWith("http://mochi.test:8888/") + || triggeringPrincipal.startsWith("http://mochi.xorigin-test:8888/"), + "initial triggeringPrincipal correct"); +ok(loadingPrincipal.startsWith("http://mochi.test:8888/") + || loadingPrincipal.startsWith("http://mochi.xorigin-test:8888/"), + "initial loadingPrincipal correct"); +ok(principalToInherit.startsWith("http://mochi.test:8888/") + || principalToInherit.startsWith("http://mochi.xorigin-test:8888/"), + "initial principalToInherit correct"); + +// reset principals on the loadinfo +loadInfo.resetPrincipalToInheritToNullPrincipal(); + +// 2) verify loadInfo contains the correct principals +triggeringPrincipal = channel.loadInfo.triggeringPrincipal.asciiSpec; +loadingPrincipal = channel.loadInfo.loadingPrincipal.asciiSpec; +principalToInherit = channel.loadInfo.principalToInherit; + +ok(triggeringPrincipal.startsWith("http://mochi.test:8888/") + || triggeringPrincipal.startsWith("http://mochi.xorigin-test:8888/"), + "triggeringPrincipal after resetting correct"); +ok(loadingPrincipal.startsWith("http://mochi.test:8888/") + || loadingPrincipal.startsWith("http://mochi.xorigin-test:8888/"), + "loadingPrincipal after resetting correct"); +ok(principalToInherit.isNullPrincipal + || principalToInherit.startsWith("http://mochi.xorigin-test:8888/"), + "principalToInherit after resetting correct"); + +// 3) verify that getChannelResultPrincipal returns right principal +var resultPrincipal = SpecialPowers.Services.scriptSecurityManager + .getChannelResultPrincipal(channel); + +ok(resultPrincipal.isNullPrincipal, + "resultPrincipal after resetting correct"); + +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_form_restoration.html b/docshell/test/mochitest/test_form_restoration.html new file mode 100644 index 0000000000..b929236770 --- /dev/null +++ b/docshell/test/mochitest/test_form_restoration.html @@ -0,0 +1,77 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test form restoration for no-store pages</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <script> + function waitForMessage(aBroadcastChannel) { + return new Promise(resolve => { + aBroadcastChannel.addEventListener("message", ({ data }) => { + resolve(data); + }, { once: true }); + }); + } + + function postMessageAndWait(aBroadcastChannel, aMsg) { + let promise = waitForMessage(aBroadcastChannel); + aBroadcastChannel.postMessage(aMsg); + return promise; + } + + async function startTest(aTestFun) { + let bc = new BroadcastChannel("form_restoration"); + + let promise = waitForMessage(bc); + window.open("file_form_restoration_no_store.html", "", "noopener"); + await promise; + + // test steps + await aTestFun(bc); + + // close broadcast channel and window + bc.postMessage("close"); + bc.close(); + } + + /* Test for bug1740517 */ + add_task(async function history_back() { + await startTest(async (aBroadcastChannel) => { + // update form data + aBroadcastChannel.postMessage("enter_data"); + + // navigate + await postMessageAndWait(aBroadcastChannel, "navigate"); + + // history back + let { persisted, formData } = await postMessageAndWait(aBroadcastChannel, "back"); + + // check form data + ok(!persisted, "Page with a no-store header shouldn't be bfcached."); + is(formData, "initial", "We shouldn't restore form data when going back to a page with a no-store header."); + }); + }); + + /* Test for bug1752250 */ + add_task(async function location_reload() { + await startTest(async (aBroadcastChannel) => { + // update form data + aBroadcastChannel.postMessage("enter_data"); + + // reload + let { persisted, formData } = await postMessageAndWait(aBroadcastChannel, "reload"); + + // check form data + ok(!persisted, "Page with a no-store header shouldn't be bfcached."); + is(formData, "initial", "We shouldn't restore form data when reload a page with a no-store header."); + }); + }); + </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/mochitest/test_framedhistoryframes.html b/docshell/test/mochitest/test_framedhistoryframes.html new file mode 100644 index 0000000000..a1bffeed1a --- /dev/null +++ b/docshell/test/mochitest/test_framedhistoryframes.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=602256 +--> +<head> + <title>Test for Bug 602256</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=602256">Mozilla Bug 602256</a> +<p id="display"></p> +<div id="content"> +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 602256 **/ + +SimpleTest.waitForExplicitFinish(); +var win = window.open("file_framedhistoryframes.html"); + +function done() { + win.close(); + SimpleTest.finish(); +} + +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_iframe_srcdoc_to_remote.html b/docshell/test/mochitest/test_iframe_srcdoc_to_remote.html new file mode 100644 index 0000000000..05e0934d50 --- /dev/null +++ b/docshell/test/mochitest/test_iframe_srcdoc_to_remote.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<head> + <meta charset="utf-8"> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" href="/tests/SimpleTest/test.css" /> +</head> + +<body onload="test()"> + <script> + /* + Test to verify that when we change an OOP iframe to one that has a + srcdoc it loads in the correct process, which in this case is this + test document. + */ + SimpleTest.waitForExplicitFinish(); + async function test() { + // Create an OOP iframe + let frame = document.createElement("iframe"); + await new Promise(r => { + frame.onload = r; + document.body.appendChild(frame); + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + frame.contentWindow.location = "http://example.net/tests/docshell/test/dummy_page.html"; + }); + if (SpecialPowers.useRemoteSubframes) { + ok(SpecialPowers.Cu.isRemoteProxy(frame.contentWindow), "should be a remote frame"); + } + + // Remove the attribute so we can set a srcdoc attribute on it + frame.removeAttribute("src"); + + // Set a srcdoc attribute on this iframe and wait for the load + await new Promise(r => { + frame.onload = r; + frame.setAttribute("srcdoc", '<html><body>body of the srcdoc frame</body></html>'); + }); + + // We should be in the same process as this test document + ok(!SpecialPowers.Cu.isRemoteProxy(frame.contentWindow), "should NOT be a remote frame"); + SimpleTest.finish(); + } + </script> +</body> + diff --git a/docshell/test/mochitest/test_javascript_sandboxed_popup.html b/docshell/test/mochitest/test_javascript_sandboxed_popup.html new file mode 100644 index 0000000000..edce93c26f --- /dev/null +++ b/docshell/test/mochitest/test_javascript_sandboxed_popup.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<head> +<meta charset="utf-8"> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" href="/tests/SimpleTest/test.css" /> +</head> + +<body> +<iframe srcdoc="<a href='javascript:opener.parent.ok(false, `The JS ran!`)' target=_blank rel=opener>click</a>" + sandbox="allow-popups allow-same-origin"></iframe> + +<script> +add_task(async function() { + let promise = new Promise(resolve =>{ + SpecialPowers.addObserver(function obs(subject) { + is(subject.opener, window[0], + "blocked javascript URI should have been targeting the pop-up document"); + subject.close(); + SpecialPowers.removeObserver(obs, "javascript-uri-blocked-by-sandbox"); + resolve(); + }, "javascript-uri-blocked-by-sandbox"); + }); + document.querySelector("iframe").contentDocument.querySelector("a").click(); + await promise; +}); +</script> +</body> diff --git a/docshell/test/mochitest/test_load_during_reload.html b/docshell/test/mochitest/test_load_during_reload.html new file mode 100644 index 0000000000..24eb409110 --- /dev/null +++ b/docshell/test/mochitest/test_load_during_reload.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test loading a new page after calling reload()</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <script> + + function promiseForLoad() { + return new Promise(resolve => { + addEventListener("message", resolve, { once: true }); + }); + } + + add_task(async function runTest() { + let win = window.open("file_load_during_reload.html"); + await promiseForLoad(); + + win.location.reload(); + win.location.href = "file_load_during_reload.html?nextpage"; + await promiseForLoad(); + + ok(win.location.href.includes("nextpage"), "Should have loaded the next page."); + win.close(); + }); + + </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/mochitest/test_navigate_after_pagehide.html b/docshell/test/mochitest/test_navigate_after_pagehide.html new file mode 100644 index 0000000000..17d58d6e62 --- /dev/null +++ b/docshell/test/mochitest/test_navigate_after_pagehide.html @@ -0,0 +1,34 @@ +<!doctype html> +<html> + <head> + <title>Test for navigation attempts by scripts in inactive inner window</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + </head> +<body> +<iframe src="dummy_page.html" id="iframe"></iframe> + +<script> +"use strict"; + +add_task(async function() { + let iframe = document.getElementById("iframe"); + + let navigate = iframe.contentWindow.eval(`(function() { + location.href = "/"; + })`); + + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + iframe.src = "http://example.com/"; + await new Promise(resolve => + iframe.addEventListener("load", resolve, { once: true }) + ); + + // This should do nothing. But, importantly, it should especially not crash. + navigate(); + + ok(true, "We didn't crash"); +}); +</script> +</body> +</html> diff --git a/docshell/test/mochitest/test_pushState_after_document_open.html b/docshell/test/mochitest/test_pushState_after_document_open.html new file mode 100644 index 0000000000..20444d8e86 --- /dev/null +++ b/docshell/test/mochitest/test_pushState_after_document_open.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=957479 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 957479</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 957479 **/ + SimpleTest.waitForExplicitFinish(); + // Child needs to invoke us, otherwise our onload will fire before the child + // has done the write/close bit. + onmessage = function doTest() { + is(frames[0].location.pathname, "/tests/docshell/test/mochitest/file_pushState_after_document_open.html", + "Should have the right path here"); + is(frames[0].location.hash, "", "Should have the right hash here"); + frames[0].history.pushState({}, "", frames[0].document.URL + "#foopy"); + is(frames[0].location.pathname, "/tests/docshell/test/mochitest/file_pushState_after_document_open.html", + "Pathname should not have changed"); + is(frames[0].location.hash, "#foopy", "Hash should have changed"); + SimpleTest.finish(); + }; + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=957479">Mozilla Bug 957479</a> +<p id="display"></p> +<div id="content" style="display: none"> +<iframe src="file_pushState_after_document_open.html"></iframe> +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/test_redirect_history.html b/docshell/test/mochitest/test_redirect_history.html new file mode 100644 index 0000000000..a67c808405 --- /dev/null +++ b/docshell/test/mochitest/test_redirect_history.html @@ -0,0 +1,58 @@ +<!doctype html> +<html> + <head> + <title>Test for redirect from POST</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> + <script> + "use strict"; + + info("Starting tests"); + + let tests = new Map([ + ["sameorigin", window.location.origin], + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + ["crossorigin", "http://test1.example.com"], + ]); + for (let [kind, origin] of tests) { + add_task(async function runTest() { + info(`Submitting to ${origin}`); + + let win; + await new Promise(resolve => { + addEventListener("message", resolve, { once: true }); + info("Loading file_redirect_history.html"); + win = window.open("file_redirect_history.html"); + }); + info("Done loading file_redirect_history.html"); + + let length = win.history.length; + let loc = win.location.toString(); + + await new Promise(resolve => { + addEventListener("message", resolve, { once: true }); + info("Posting"); + win.postMessage(`${origin}/tests/docshell/test/mochitest/form_submit_redirect.sjs?redirectTo=${loc}`, "*") + }); + info("Done posting\n"); + is(win.history.length, length, `Test ${kind}: history length should not change.`); + info(`Length=${win.history.length}`); + is(win.location.toString(), loc, `Test ${kind}: location should not change.`); + + await new Promise(resolve => { + addEventListener("message", resolve, { once: true }); + info("Reloading"); + win.location.reload(); + }); + info("Done reloading\n"); + is(win.location.toString(), loc, `Test ${kind}: location should not change after reload.`); + + win.close(); + }); + } + </script> + </body> +</html> diff --git a/docshell/test/mochitest/test_triggeringprincipal_location_seturi.html b/docshell/test/mochitest/test_triggeringprincipal_location_seturi.html new file mode 100644 index 0000000000..92e20b03ea --- /dev/null +++ b/docshell/test/mochitest/test_triggeringprincipal_location_seturi.html @@ -0,0 +1,105 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + +<script type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +const SAME_ORIGIN_URI = "http://mochi.test:8888/tests/docshell/test/dummy_page.html"; +// eslint-disable-next-line @microsoft/sdl/no-insecure-url +const CROSS_ORIGIN_URI = "http://example.com/tests/docshell/test/dummy_page.html"; +const NUMBER_OF_TESTS = 3; +let testCounter = 0; + +function checkFinish() { + testCounter++; + if (testCounter < NUMBER_OF_TESTS) { + return; + } + SimpleTest.finish(); +} + +// ---- test 1 ---- + +let myFrame1 = document.createElement("iframe"); +myFrame1.src = SAME_ORIGIN_URI; +myFrame1.addEventListener("load", checkLoadFrame1); +document.documentElement.appendChild(myFrame1); + +function checkLoadFrame1() { + myFrame1.removeEventListener("load", checkLoadFrame1); + // window.location.href is no longer cross-origin accessible in gecko. + is(SpecialPowers.wrap(myFrame1.contentWindow).location.href, SAME_ORIGIN_URI, + "initial same origin dummy loaded into frame1"); + + SpecialPowers.wrap(myFrame1.contentWindow).location.hash = "#bar"; + is(SpecialPowers.wrap(myFrame1.contentWindow).location.href, SAME_ORIGIN_URI + "#bar", + "initial same origin dummy#bar loaded into iframe1"); + + myFrame1.addEventListener("load", checkNavFrame1); + myFrame1.src = CROSS_ORIGIN_URI; +} + +async function checkNavFrame1() { + myFrame1.removeEventListener("load", checkNavFrame1); + is(await SpecialPowers.spawn(myFrame1, [], () => this.content.location.href), + CROSS_ORIGIN_URI, + "cross origin dummy loaded into frame1"); + + myFrame1.addEventListener("load", checkBackNavFrame1); + myFrame1.src = SAME_ORIGIN_URI + "#bar"; +} + +async function checkBackNavFrame1() { + myFrame1.removeEventListener("load", checkBackNavFrame1); + is(await SpecialPowers.spawn(myFrame1, [], () => this.content.location.href), + SAME_ORIGIN_URI + "#bar", + "navagiating back to same origin dummy for frame1"); + checkFinish(); +} + +// ---- test 2 ---- + +let myFrame2 = document.createElement("iframe"); +myFrame2.src = "about:blank"; +myFrame2.addEventListener("load", checkLoadFrame2); +document.documentElement.appendChild(myFrame2); + +function checkLoadFrame2() { + myFrame2.removeEventListener("load", checkLoadFrame2); + is(SpecialPowers.wrap(myFrame2.contentWindow).location.href, "about:blank", + "initial about:blank frame loaded"); + + myFrame2.contentWindow.location.hash = "#foo"; + is(SpecialPowers.wrap(myFrame2.contentWindow).location.href, "about:blank#foo", + "about:blank#foo frame loaded"); + + myFrame2.addEventListener("load", checkHistoryFrame2); + myFrame2.src = "about:blank"; +} + +function checkHistoryFrame2() { + myFrame2.removeEventListener("load", checkHistoryFrame2); + is(SpecialPowers.wrap(myFrame2.contentWindow).location.href, "about:blank", + "about:blank frame loaded again"); + checkFinish(); +} + +// ---- test 3 ---- + +let myFrame3 = document.createElement("frame"); +document.documentElement.appendChild(myFrame3); +myFrame3.contentWindow.location.hash = "#foo"; + +is(myFrame3.contentWindow.location.href, "about:blank#foo", + "created history entry with about:blank#foo"); +checkFinish(); + +</script> +</body> +</html> diff --git a/docshell/test/mochitest/test_windowedhistoryframes.html b/docshell/test/mochitest/test_windowedhistoryframes.html new file mode 100644 index 0000000000..c2c148b838 --- /dev/null +++ b/docshell/test/mochitest/test_windowedhistoryframes.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=602256 +--> +<head> + <title>Test for Bug 602256</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=602256">Mozilla Bug 602256</a> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 602256 **/ + +SimpleTest.waitForExplicitFinish(); + +function done() { + subWin.close(); + SimpleTest.finish(); +} + +var subWin = window.open("historyframes.html", "_blank"); + +</script> +</pre> +</body> +</html> diff --git a/docshell/test/mochitest/url1_historyframe.html b/docshell/test/mochitest/url1_historyframe.html new file mode 100644 index 0000000000..b86af4b3fa --- /dev/null +++ b/docshell/test/mochitest/url1_historyframe.html @@ -0,0 +1 @@ +<p id='text'>Test1</p> diff --git a/docshell/test/mochitest/url2_historyframe.html b/docshell/test/mochitest/url2_historyframe.html new file mode 100644 index 0000000000..24374d1a5b --- /dev/null +++ b/docshell/test/mochitest/url2_historyframe.html @@ -0,0 +1 @@ +<p id='text'>Test2</p> diff --git a/docshell/test/moz.build b/docshell/test/moz.build new file mode 100644 index 0000000000..7cebe0339f --- /dev/null +++ b/docshell/test/moz.build @@ -0,0 +1,137 @@ +with Files("**"): + BUG_COMPONENT = ("Core", "DOM: Navigation") + +with Files("browser/*_bug234628*"): + BUG_COMPONENT = ("Core", "Internationalization") + +with Files("browser/*_bug349769*"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +with Files("browser/*_bug388121*"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +with Files("browser/*_bug655270*"): + BUG_COMPONENT = ("Toolkit", "Places") + +with Files("browser/*_bug655273*"): + BUG_COMPONENT = ("Firefox", "Menus") + +with Files("browser/*_bug852909*"): + BUG_COMPONENT = ("Firefox", "Menus") + +with Files("browser/*bug92473*"): + BUG_COMPONENT = ("Core", "Internationalization") + +with Files("browser/*loadDisallowInherit*"): + BUG_COMPONENT = ("Firefox", "Address Bar") + +with Files("browser/*tab_touch_events*"): + BUG_COMPONENT = ("Core", "DOM: Events") + +with Files("browser/*timelineMarkers*"): + BUG_COMPONENT = ("DevTools", "Performance Tools (Profiler/Timeline)") + +with Files("browser/*ua_emulation*"): + BUG_COMPONENT = ("DevTools", "General") + +with Files("chrome/*112564*"): + BUG_COMPONENT = ("Core", "Networking: HTTP") + +with Files("chrome/*303267*"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +with Files("chrome/*453650*"): + BUG_COMPONENT = ("Core", "Layout") + +with Files("chrome/*565388*"): + BUG_COMPONENT = ("Core", "Widget") + +with Files("chrome/*582176*"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +with Files("chrome/*608669*"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +with Files("chrome/*690056*"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +with Files("chrome/*92598*"): + BUG_COMPONENT = ("Core", "Networking: HTTP") + +with Files("iframesandbox/**"): + BUG_COMPONENT = ("Core", "Security") + +with Files("iframesandbox/*marquee_event_handlers*"): + BUG_COMPONENT = ("Core", "DOM: Security") + + +with Files("mochitest/*1045096*"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +with Files("mochitest/*1151421*"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +with Files("mochitest/*402210*"): + BUG_COMPONENT = ("Core", "DOM: Security") + +with Files("mochitest/*509055*"): + BUG_COMPONENT = ("Firefox", "Bookmarks & History") + +with Files("mochitest/*511449*"): + BUG_COMPONENT = ("Core", "Widget: Cocoa") + +with Files("mochitest/*551225*"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +with Files("mochitest/*570341*"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +with Files("mochitest/*580069*"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +with Files("mochitest/*637644*"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +with Files("mochitest/*640387*"): + BUG_COMPONENT = ("Core", "DOM: Events") + +with Files("mochitest/*668513*"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +with Files("mochitest/*797909*"): + BUG_COMPONENT = ("Core", "DOM: Core & HTML") + +with Files("mochitest/*forceinheritprincipal*"): + BUG_COMPONENT = ("Core", "DOM: Security") + + +with Files("navigation/*13871.html"): + BUG_COMPONENT = ("Core", "Security") + +with Files("navigation/*386782*"): + BUG_COMPONENT = ("Core", "DOM: Editor") + +with Files("navigation/*430624*"): + BUG_COMPONENT = ("Core", "DOM: Editor") + +with Files("navigation/*430723*"): + BUG_COMPONENT = ("Core", "DOM: UI Events & Focus Handling") + +with Files("navigation/*child*"): + BUG_COMPONENT = ("Core", "Security") + +with Files("navigation/*opener*"): + BUG_COMPONENT = ("Core", "Security") + +with Files("navigation/*reserved*"): + BUG_COMPONENT = ("Core", "Security") + +with Files("navigation/*triggering*"): + BUG_COMPONENT = ("Core", "DOM: Security") + + +with Files("unit/*442584*"): + BUG_COMPONENT = ("Core", "Networking: Cache") + +with Files("unit/*setUsePrivateBrowsing*"): + BUG_COMPONENT = ("Firefox", "Extension Compatibility") 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..2c960bbf45 --- /dev/null +++ b/docshell/test/navigation/browser_bug1757458.js @@ -0,0 +1,45 @@ +/* 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.loadURI(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..dd6fbdca41 --- /dev/null +++ b/docshell/test/navigation/browser_bug343515.js @@ -0,0 +1,267 @@ +// 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.loadURI(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.loadURI(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..4034f63bd9 --- /dev/null +++ b/docshell/test/navigation/browser_ghistorymaxsize_is_0.js @@ -0,0 +1,81 @@ +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.loadURI(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..af527b2dbc --- /dev/null +++ b/docshell/test/navigation/browser_test-content-chromeflags.js @@ -0,0 +1,57 @@ +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..fe0db2742b --- /dev/null +++ b/docshell/test/navigation/browser_test_bfcache_eviction.js @@ -0,0 +1,98 @@ +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.loadURI(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..a4a78ac961 --- /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.loadURI(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..559cc6180c --- /dev/null +++ b/docshell/test/navigation/browser_test_simultaneous_normal_and_history_loads.js @@ -0,0 +1,53 @@ +/* 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.loadURI(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(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'></iframe>"></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..7070cdbefc --- /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;"> + </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;"> + </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;"> + </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..032d5a8471 --- /dev/null +++ b/docshell/test/navigation/mochitest.ini @@ -0,0 +1,219 @@ +[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] +[test_bug1583110.html] +support-files = file_bug1583110.html +[test_bug1706090.html] +support-files = file_bug1706090.html +skip-if = fission # 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] +[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 +[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 +[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] +[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] +[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 +[test_popup-navigates-children.html] +[test_reload.html] +support-files = file_reload.html +[test_reload_nonbfcached_srcdoc.html] +support-files = file_reload_nonbfcached_srcdoc.sjs +[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] +[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] +[test_triggeringprincipal_frame_nav.html] +[test_triggeringprincipal_frame_same_origin_nav.html] +[test_triggeringprincipal_window_open.html] +[test_triggeringprincipal_parent_iframe_window_open.html] +[test_triggeringprincipal_iframe_iframe_window_open.html] +[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] +[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 +[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..6e9d8b570e --- /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..5989f633e9 --- /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.sessionHistoryInParent) { + 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..04decf6815 --- /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.fissionAutostart) { + // 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..76967b7b17 --- /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..895b1e49eb --- /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..fbdc5d2677 --- /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..c2ba4b41c3 --- /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..c129824537 --- /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 <iframe id="static" src="http://example.com/tests/docshell/test/navigation/file_nested_srcdoc.html"></iframe>"></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> diff --git a/docshell/test/unit/AllowJavascriptChild.sys.mjs b/docshell/test/unit/AllowJavascriptChild.sys.mjs new file mode 100644 index 0000000000..a7c3fa6172 --- /dev/null +++ b/docshell/test/unit/AllowJavascriptChild.sys.mjs @@ -0,0 +1,41 @@ +export class AllowJavascriptChild extends JSWindowActorChild { + async receiveMessage(msg) { + switch (msg.name) { + case "CheckScriptsAllowed": + return this.checkScriptsAllowed(); + case "CheckFiredLoadEvent": + return this.contentWindow.wrappedJSObject.gFiredOnload; + case "CreateIframe": + return this.createIframe(msg.data.url); + } + return null; + } + + handleEvent(event) { + if (event.type === "load") { + this.sendAsyncMessage("LoadFired"); + } + } + + checkScriptsAllowed() { + let win = this.contentWindow; + + win.wrappedJSObject.gFiredOnclick = false; + win.document.body.click(); + return win.wrappedJSObject.gFiredOnclick; + } + + async createIframe(url) { + let doc = this.contentWindow.document; + + let iframe = doc.createElement("iframe"); + iframe.src = url; + doc.body.appendChild(iframe); + + await new Promise(resolve => { + iframe.addEventListener("load", resolve, { once: true }); + }); + + return iframe.browsingContext; + } +} diff --git a/docshell/test/unit/AllowJavascriptParent.sys.mjs b/docshell/test/unit/AllowJavascriptParent.sys.mjs new file mode 100644 index 0000000000..5631fcdb09 --- /dev/null +++ b/docshell/test/unit/AllowJavascriptParent.sys.mjs @@ -0,0 +1,28 @@ +let loadPromises = new WeakMap(); + +export class AllowJavascriptParent extends JSWindowActorParent { + async receiveMessage(msg) { + switch (msg.name) { + case "LoadFired": + let bc = this.browsingContext; + let deferred = loadPromises.get(bc); + if (deferred) { + loadPromises.delete(bc); + deferred.resolve(this); + } + break; + } + } + + static promiseLoad(bc) { + let deferred = loadPromises.get(bc); + if (!deferred) { + deferred = {}; + deferred.promise = new Promise(resolve => { + deferred.resolve = resolve; + }); + loadPromises.set(bc, deferred); + } + return deferred.promise; + } +} diff --git a/docshell/test/unit/data/engine.xml b/docshell/test/unit/data/engine.xml new file mode 100644 index 0000000000..3a2bd85c1b --- /dev/null +++ b/docshell/test/unit/data/engine.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/"> +<ShortName>test_urifixup_search_engine</ShortName> +<Description>test_urifixup_search_engine</Description> +<InputEncoding>UTF-8</InputEncoding> +<Url type="text/html" method="GET" template="https://www.example.org/"> + <Param name="search" value="{searchTerms}"/> +</Url> +<SearchForm>https://www.example.org/</SearchForm> +</SearchPlugin> diff --git a/docshell/test/unit/data/enginePost.xml b/docshell/test/unit/data/enginePost.xml new file mode 100644 index 0000000000..14775b6f0a --- /dev/null +++ b/docshell/test/unit/data/enginePost.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/"> +<ShortName>test_urifixup_search_engine_post</ShortName> +<Description>test_urifixup_search_engine_post</Description> +<InputEncoding>UTF-8</InputEncoding> +<Url type="text/html" method="POST" template="https://www.example.org/"> + <Param name="q" value="{searchTerms}"/> +</Url> +<SearchForm>https://www.example.org/</SearchForm> +</SearchPlugin> diff --git a/docshell/test/unit/data/enginePrivate.xml b/docshell/test/unit/data/enginePrivate.xml new file mode 100644 index 0000000000..7d87de98fa --- /dev/null +++ b/docshell/test/unit/data/enginePrivate.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/"> +<ShortName>test_urifixup_search_engine_private</ShortName> +<Description>test_urifixup_search_engine_private</Description> +<InputEncoding>UTF-8</InputEncoding> +<Url type="text/html" method="GET" template="https://www.example.org/"> + <Param name="private" value="{searchTerms}"/> +</Url> +<SearchForm>https://www.example.org/</SearchForm> +</SearchPlugin> diff --git a/docshell/test/unit/head_docshell.js b/docshell/test/unit/head_docshell.js new file mode 100644 index 0000000000..9e2af04b6b --- /dev/null +++ b/docshell/test/unit/head_docshell.js @@ -0,0 +1,106 @@ +/* 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/. */ + +var { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + SearchTestUtils: "resource://testing-common/SearchTestUtils.sys.mjs", + SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs", + TestUtils: "resource://testing-common/TestUtils.sys.mjs", +}); + +XPCOMUtils.defineLazyModuleGetters(this, { + AddonTestUtils: "resource://testing-common/AddonTestUtils.jsm", + HttpServer: "resource://testing-common/httpd.js", + NetUtil: "resource://gre/modules/NetUtil.jsm", +}); + +var profileDir = do_get_profile(); + +const kSearchEngineID = "test_urifixup_search_engine"; +const kSearchEngineURL = "https://www.example.org/?search={searchTerms}"; +const kPrivateSearchEngineID = "test_urifixup_search_engine_private"; +const kPrivateSearchEngineURL = + "https://www.example.org/?private={searchTerms}"; +const kPostSearchEngineID = "test_urifixup_search_engine_post"; +const kPostSearchEngineURL = "https://www.example.org/"; +const kPostSearchEngineData = "q={searchTerms}"; + +const SEARCH_CONFIG = [ + { + appliesTo: [ + { + included: { + everywhere: true, + }, + }, + ], + default: "yes", + webExtension: { + id: "fixup_search@search.mozilla.org", + }, + }, +]; + +async function setupSearchService() { + SearchTestUtils.init(this); + + AddonTestUtils.init(this); + AddonTestUtils.overrideCertDB(); + AddonTestUtils.createAppInfo( + "xpcshell@tests.mozilla.org", + "XPCShell", + "1", + "42" + ); + + await SearchTestUtils.useTestEngines(".", null, SEARCH_CONFIG); + await AddonTestUtils.promiseStartupManager(); + await Services.search.init(); +} + +/** + * After useHttpServer() is called, this string contains the URL of the "data" + * directory, including the final slash. + */ +var gDataUrl; + +/** + * Initializes the HTTP server and ensures that it is terminated when tests end. + * + * @param {string} dir + * The test sub-directory to use for the engines. + * @returns {HttpServer} + * The HttpServer object in case further customization is needed. + */ +function useHttpServer(dir = "data") { + let httpServer = new HttpServer(); + httpServer.start(-1); + httpServer.registerDirectory("/", do_get_cwd()); + gDataUrl = `http://localhost:${httpServer.identity.primaryPort}/${dir}/`; + registerCleanupFunction(async function cleanup_httpServer() { + await new Promise(resolve => { + httpServer.stop(resolve); + }); + }); + return httpServer; +} + +async function addTestEngines() { + useHttpServer(); + // This is a hack, ideally we should be setting up a configuration with + // built-in engines, but the `chrome_settings_overrides` section that + // WebExtensions need is only defined for browser/ + await SearchTestUtils.promiseNewSearchEngine({ + url: `${gDataUrl}/engine.xml`, + }); + await SearchTestUtils.promiseNewSearchEngine({ + url: `${gDataUrl}/enginePrivate.xml`, + }); + await SearchTestUtils.promiseNewSearchEngine({ + url: `${gDataUrl}/enginePost.xml`, + }); +} diff --git a/docshell/test/unit/test_URIFixup.js b/docshell/test/unit/test_URIFixup.js new file mode 100644 index 0000000000..7967933b56 --- /dev/null +++ b/docshell/test/unit/test_URIFixup.js @@ -0,0 +1,123 @@ +var pref = "browser.fixup.typo.scheme"; + +var data = [ + { + // ttp -> http. + wrong: "ttp://www.example.com/", + fixed: "http://www.example.com/", + }, + { + // htp -> http. + wrong: "htp://www.example.com/", + fixed: "http://www.example.com/", + }, + { + // ttps -> https. + wrong: "ttps://www.example.com/", + fixed: "https://www.example.com/", + }, + { + // tps -> https. + wrong: "tps://www.example.com/", + fixed: "https://www.example.com/", + }, + { + // ps -> https. + wrong: "ps://www.example.com/", + fixed: "https://www.example.com/", + }, + { + // htps -> https. + wrong: "htps://www.example.com/", + fixed: "https://www.example.com/", + }, + { + // ile -> file. + wrong: "ile:///this/is/a/test.html", + fixed: "file:///this/is/a/test.html", + }, + { + // le -> file. + wrong: "le:///this/is/a/test.html", + fixed: "file:///this/is/a/test.html", + }, + { + // Replace ';' with ':'. + wrong: "http;//www.example.com/", + fixed: "http://www.example.com/", + noPrefValue: "http://http;//www.example.com/", + }, + { + // Missing ':'. + wrong: "https//www.example.com/", + fixed: "https://www.example.com/", + noPrefValue: "http://https//www.example.com/", + }, + { + // Missing ':' for file scheme. + wrong: "file///this/is/a/test.html", + fixed: "file:///this/is/a/test.html", + noPrefValue: "http://file///this/is/a/test.html", + }, + { + // Valid should not be changed. + wrong: "https://example.com/this/is/a/test.html", + fixed: "https://example.com/this/is/a/test.html", + }, + { + // Unmatched should not be changed. + wrong: "whatever://this/is/a/test.html", + fixed: "whatever://this/is/a/test.html", + }, +]; + +var len = data.length; + +add_task(async function setup() { + await setupSearchService(); + // Now we've initialised the search service, we force remove the engines + // it has, so they don't interfere with this test. + // Search engine integration is tested in test_URIFixup_search.js. + Services.search.wrappedJSObject._engines.clear(); +}); + +// Make sure we fix what needs fixing when there is no pref set. +add_task(function test_unset_pref_fixes_typos() { + Services.prefs.clearUserPref(pref); + for (let i = 0; i < len; ++i) { + let item = data[i]; + let { preferredURI } = Services.uriFixup.getFixupURIInfo( + item.wrong, + Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS + ); + Assert.equal(preferredURI.spec, item.fixed); + } +}); + +// Make sure we don't do anything when the pref is explicitly +// set to false. +add_task(function test_false_pref_keeps_typos() { + Services.prefs.setBoolPref(pref, false); + for (let i = 0; i < len; ++i) { + let item = data[i]; + let { preferredURI } = Services.uriFixup.getFixupURIInfo( + item.wrong, + Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS + ); + Assert.equal(preferredURI.spec, item.noPrefValue || item.wrong); + } +}); + +// Finally, make sure we still fix what needs fixing if the pref is +// explicitly set to true. +add_task(function test_true_pref_fixes_typos() { + Services.prefs.setBoolPref(pref, true); + for (let i = 0; i < len; ++i) { + let item = data[i]; + let { preferredURI } = Services.uriFixup.getFixupURIInfo( + item.wrong, + Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS + ); + Assert.equal(preferredURI.spec, item.fixed); + } +}); diff --git a/docshell/test/unit/test_URIFixup_check_host.js b/docshell/test/unit/test_URIFixup_check_host.js new file mode 100644 index 0000000000..3331176082 --- /dev/null +++ b/docshell/test/unit/test_URIFixup_check_host.js @@ -0,0 +1,183 @@ +/* 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/. */ + +"use strict"; + +const lazy = {}; + +// Ensure DNS lookups don't hit the server +XPCOMUtils.defineLazyServiceGetter( + lazy, + "gDNSOverride", + "@mozilla.org/network/native-dns-override;1", + "nsINativeDNSResolverOverride" +); + +add_task(async function setup() { + Services.prefs.setStringPref("browser.fixup.alternate.prefix", "www."); + Services.prefs.setStringPref("browser.fixup.alternate.suffix", ".com"); + Services.prefs.setStringPref("browser.fixup.alternate.protocol", "https"); + Services.prefs.setBoolPref( + "browser.urlbar.dnsResolveFullyQualifiedNames", + true + ); + registerCleanupFunction(function() { + Services.prefs.clearUserPref("browser.fixup.alternate.prefix"); + Services.prefs.clearUserPref("browser.fixup.alternate.suffix"); + Services.prefs.clearUserPref("browser.fixup.alternate.protocol"); + Services.prefs.clearUserPref( + "browser.urlbar.dnsResolveFullyQualifiedNames" + ); + }); +}); + +// TODO: Export Listener from test_dns_override instead of duping +class Listener { + constructor() { + this.promise = new Promise(resolve => { + this.resolve = resolve; + }); + } + + onLookupComplete(inRequest, inRecord, inStatus) { + this.resolve([inRequest, inRecord, inStatus]); + } + + async firstAddress() { + let all = await this.addresses(); + if (all.length) { + return all[0]; + } + return undefined; + } + + async addresses() { + let [, inRecord] = await this.promise; + let addresses = []; + if (!inRecord) { + return addresses; // returns [] + } + inRecord.QueryInterface(Ci.nsIDNSAddrRecord); + while (inRecord.hasMore()) { + addresses.push(inRecord.getNextAddrAsString()); + } + return addresses; + } + + then() { + return this.promise.then.apply(this.promise, arguments); + } +} + +const FAKE_IP = "::1"; +const FAKE_INTRANET_IP = "::2"; +const ORIGIN_ATTRIBUTE = {}; + +add_task(async function test_uri_with_force_fixup() { + let listener = new Listener(); + let { fixedURI } = Services.uriFixup.forceHttpFixup("http://www.example.com"); + + lazy.gDNSOverride.addIPOverride(fixedURI.displayHost, FAKE_IP); + + Services.uriFixup.checkHost(fixedURI, listener, ORIGIN_ATTRIBUTE); + Assert.equal( + await listener.firstAddress(), + FAKE_IP, + "Should've received fake IP" + ); + + lazy.gDNSOverride.clearHostOverride(fixedURI.displayHost); + Services.dns.clearCache(false); +}); + +add_task(async function test_uri_with_get_fixup() { + let listener = new Listener(); + let uri = Services.io.newURI("http://www.example.com"); + + lazy.gDNSOverride.addIPOverride(uri.displayHost, FAKE_IP); + + Services.uriFixup.checkHost(uri, listener, ORIGIN_ATTRIBUTE); + Assert.equal( + await listener.firstAddress(), + FAKE_IP, + "Should've received fake IP" + ); + + lazy.gDNSOverride.clearHostOverride(uri.displayHost); + Services.dns.clearCache(false); +}); + +add_task(async function test_intranet_like_uri() { + let listener = new Listener(); + let uri = Services.io.newURI("http://someintranet"); + + lazy.gDNSOverride.addIPOverride(uri.displayHost, FAKE_IP); + // Hosts without periods should end with a period to make them FQN + lazy.gDNSOverride.addIPOverride(uri.displayHost + ".", FAKE_INTRANET_IP); + + Services.uriFixup.checkHost(uri, listener, ORIGIN_ATTRIBUTE); + Assert.deepEqual( + await listener.addresses(), + FAKE_INTRANET_IP, + "Should've received fake intranet IP" + ); + + lazy.gDNSOverride.clearHostOverride(uri.displayHost); + lazy.gDNSOverride.clearHostOverride(uri.displayHost + "."); + Services.dns.clearCache(false); +}); + +add_task(async function test_intranet_like_uri_without_fixup() { + let listener = new Listener(); + let uri = Services.io.newURI("http://someintranet"); + Services.prefs.setBoolPref( + "browser.urlbar.dnsResolveFullyQualifiedNames", + false + ); + + lazy.gDNSOverride.addIPOverride(uri.displayHost, FAKE_IP); + // Hosts without periods should end with a period to make them FQN + lazy.gDNSOverride.addIPOverride(uri.displayHost + ".", FAKE_INTRANET_IP); + + Services.uriFixup.checkHost(uri, listener, ORIGIN_ATTRIBUTE); + Assert.deepEqual( + await listener.addresses(), + FAKE_IP, + "Should've received non-fixed up IP" + ); + + lazy.gDNSOverride.clearHostOverride(uri.displayHost); + lazy.gDNSOverride.clearHostOverride(uri.displayHost + "."); + Services.dns.clearCache(false); +}); + +add_task(async function test_ip_address() { + let listener = new Listener(); + let uri = Services.io.newURI("http://192.168.0.1"); + + // Testing IP address being passed into the method + // requires observing if the asyncResolve method gets called + let didResolve = false; + let topic = "uri-fixup-check-dns"; + let observer = { + observe(aSubject, aTopicInner, aData) { + if (aTopicInner == topic) { + didResolve = true; + } + }, + }; + Services.obs.addObserver(observer, topic); + lazy.gDNSOverride.addIPOverride(uri.displayHost, FAKE_IP); + + Services.uriFixup.checkHost(uri, listener, ORIGIN_ATTRIBUTE); + Assert.equal( + didResolve, + false, + "Passing an IP address should not conduct a host lookup" + ); + + lazy.gDNSOverride.clearHostOverride(uri.displayHost); + Services.dns.clearCache(false); + Services.obs.removeObserver(observer, topic); +}); diff --git a/docshell/test/unit/test_URIFixup_external_protocol_fallback.js b/docshell/test/unit/test_URIFixup_external_protocol_fallback.js new file mode 100644 index 0000000000..5d37be55fb --- /dev/null +++ b/docshell/test/unit/test_URIFixup_external_protocol_fallback.js @@ -0,0 +1,107 @@ +/* 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/. */ + +"use strict"; + +// Test whether fallback mechanism is working if don't trust nsIExternalProtocolService. + +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); +const { MockRegistrar } = ChromeUtils.importESModule( + "resource://testing-common/MockRegistrar.sys.mjs" +); + +add_task(async function setup() { + info( + "Prepare mock nsIExternalProtocolService whose externalProtocolHandlerExists returns always true" + ); + const externalProtocolService = Cc[ + "@mozilla.org/uriloader/external-protocol-service;1" + ].getService(Ci.nsIExternalProtocolService); + const mockId = MockRegistrar.register( + "@mozilla.org/uriloader/external-protocol-service;1", + { + getProtocolHandlerInfo: scheme => + externalProtocolService.getProtocolHandlerInfo(scheme), + externalProtocolHandlerExists: () => true, + QueryInterface: ChromeUtils.generateQI(["nsIExternalProtocolService"]), + } + ); + const mockExternalProtocolService = Cc[ + "@mozilla.org/uriloader/external-protocol-service;1" + ].getService(Ci.nsIExternalProtocolService); + Assert.ok( + mockExternalProtocolService.externalProtocolHandlerExists("__invalid__"), + "Mock service is working" + ); + + info("Register new dummy protocol"); + const dummyProtocolHandlerInfo = externalProtocolService.getProtocolHandlerInfo( + "dummy" + ); + const handlerService = Cc[ + "@mozilla.org/uriloader/handler-service;1" + ].getService(Ci.nsIHandlerService); + handlerService.store(dummyProtocolHandlerInfo); + + info("Prepare test search engine"); + await setupSearchService(); + await addTestEngines(); + await Services.search.setDefault( + Services.search.getEngineByName(kSearchEngineID), + Ci.nsISearchService.CHANGE_REASON_UNKNOWN + ); + + registerCleanupFunction(() => { + handlerService.remove(dummyProtocolHandlerInfo); + MockRegistrar.unregister(mockId); + }); +}); + +add_task(function basic() { + const testData = [ + { + input: "mailto:test@example.com", + expected: + isSupportedInHandlerService("mailto") || + // Thunderbird IS a mailto handler, it doesn't have handlers. + AppConstants.MOZ_APP_NAME == "thunderbird" + ? "mailto:test@example.com" + : "http://mailto:test@example.com/", + }, + { + input: "keyword:search", + expected: "https://www.example.org/?search=keyword%3Asearch", + }, + { + input: "dummy:protocol", + expected: "dummy:protocol", + }, + ]; + + for (const { input, expected } of testData) { + assertFixup(input, expected); + } +}); + +function assertFixup(input, expected) { + const { preferredURI } = Services.uriFixup.getFixupURIInfo( + input, + Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS + ); + Assert.equal(preferredURI.spec, expected); +} + +function isSupportedInHandlerService(scheme) { + const externalProtocolService = Cc[ + "@mozilla.org/uriloader/external-protocol-service;1" + ].getService(Ci.nsIExternalProtocolService); + const handlerService = Cc[ + "@mozilla.org/uriloader/handler-service;1" + ].getService(Ci.nsIHandlerService); + return handlerService.exists( + externalProtocolService.getProtocolHandlerInfo(scheme) + ); +} diff --git a/docshell/test/unit/test_URIFixup_forced.js b/docshell/test/unit/test_URIFixup_forced.js new file mode 100644 index 0000000000..dad24aaa99 --- /dev/null +++ b/docshell/test/unit/test_URIFixup_forced.js @@ -0,0 +1,162 @@ +/* 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/. */ + +"use strict"; + +var data = [ + { + // singleword. + wrong: "http://example/", + fixed: "https://www.example.com/", + }, + { + // upgrade protocol. + wrong: "http://www.example.com/", + fixed: "https://www.example.com/", + noAlternateURI: true, + }, + { + // no difference. + wrong: "https://www.example.com/", + fixed: "https://www.example.com/", + noProtocolFixup: true, + noAlternateURI: true, + }, + { + // don't add prefix when there's more than one dot. + wrong: "http://www.example.abc.def/", + fixed: "https://www.example.abc.def/", + noAlternateURI: true, + }, + { + // http -> https. + wrong: "http://www.example/", + fixed: "https://www.example/", + noAlternateURI: true, + }, + { + // domain.com -> https://www.domain.com. + wrong: "http://example.com/", + fixed: "https://www.example.com/", + }, + { + // example/example... -> https://www.example.com/example/ + wrong: "http://example/example/", + fixed: "https://www.example.com/example/", + }, + { + // example/example/s#q -> www.example.com/example/s#q. + wrong: "http://example/example/s#q", + fixed: "https://www.example.com/example/s#q", + }, + { + wrong: "http://モジラ.org", + fixed: "https://www.xn--yck6dwa.org/", + }, + { + wrong: "http://モジラ", + fixed: "https://www.xn--yck6dwa.com/", + }, + { + wrong: "http://xn--yck6dwa", + fixed: "https://www.xn--yck6dwa.com/", + }, + { + wrong: "https://モジラ.org", + fixed: "https://www.xn--yck6dwa.org/", + noProtocolFixup: true, + }, + { + wrong: "https://モジラ", + fixed: "https://www.xn--yck6dwa.com/", + noProtocolFixup: true, + }, + { + wrong: "https://xn--yck6dwa", + fixed: "https://www.xn--yck6dwa.com/", + noProtocolFixup: true, + }, + { + // Host is https -> fixup typos of protocol + wrong: "htp://https://mozilla.org", + fixed: "http://https//mozilla.org", + noAlternateURI: true, + }, + { + // Host is http -> fixup typos of protocol + wrong: "ttp://http://mozilla.org", + fixed: "http://http//mozilla.org", + noAlternateURI: true, + }, + { + // Host is localhost -> fixup typos of protocol + wrong: "htps://localhost://mozilla.org", + fixed: "https://localhost//mozilla.org", + noAlternateURI: true, + }, + { + // view-source is not http/https -> error + wrong: "view-source:http://example/example/example/example", + reject: true, + comment: "Scheme should be either http or https", + }, + { + // file is not http/https -> error + wrong: "file://http://example/example/example/example", + reject: true, + comment: "Scheme should be either http or https", + }, + { + // Protocol is missing -> error + wrong: "example.com", + reject: true, + comment: "Scheme should be either http or https", + }, + { + // Empty input -> error + wrong: "", + reject: true, + comment: "Should pass a non-null uri", + }, +]; + +add_task(async function setup() { + Services.prefs.setStringPref("browser.fixup.alternate.prefix", "www."); + Services.prefs.setStringPref("browser.fixup.alternate.suffix", ".com"); + Services.prefs.setStringPref("browser.fixup.alternate.protocol", "https"); + registerCleanupFunction(function() { + Services.prefs.clearUserPref("browser.fixup.alternate.prefix"); + Services.prefs.clearUserPref("browser.fixup.alternate.suffix"); + Services.prefs.clearUserPref("browser.fixup.alternate.protocol"); + }); +}); + +add_task(function test_default_https_pref() { + for (let item of data) { + if (item.reject) { + Assert.throws( + () => Services.uriFixup.forceHttpFixup(item.wrong), + /NS_ERROR_FAILURE/, + item.comment + ); + } else { + let { + fixupChangedProtocol, + fixupCreatedAlternateURI, + fixedURI, + } = Services.uriFixup.forceHttpFixup(item.wrong); + Assert.equal(fixedURI.spec, item.fixed, "Specs should be the same"); + Assert.equal( + fixupChangedProtocol, + !item.noProtocolFixup, + `fixupChangedProtocol should be ${!item.noAlternateURI}` + ); + Assert.equal( + fixupCreatedAlternateURI, + !item.noAlternateURI, + `fixupCreatedAlternateURI should be ${!item.limitedFixup}` + ); + } + } +}); diff --git a/docshell/test/unit/test_URIFixup_info.js b/docshell/test/unit/test_URIFixup_info.js new file mode 100644 index 0000000000..89514cb00c --- /dev/null +++ b/docshell/test/unit/test_URIFixup_info.js @@ -0,0 +1,1075 @@ +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); + +const kForceDNSLookup = "browser.fixup.dns_first_for_single_words"; +const kFixupEnabled = "browser.fixup.alternate.enabled"; + +// TODO(bug 1522134), this test should also use +// combinations of the following flags. +var flagInputs = [ + Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP, + Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP | + Services.uriFixup.FIXUP_FLAG_PRIVATE_CONTEXT, + Services.uriFixup.FIXUP_FLAGS_MAKE_ALTERNATE_URI, + Services.uriFixup.FIXUP_FLAG_FORCE_ALTERNATE_URI, + Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS, + // This should not really generate a search, but it does, see Bug 1588118. + Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS | + Services.uriFixup.FIXUP_FLAG_PRIVATE_CONTEXT, +]; + +/* + The following properties are supported for these test cases: + { + input: "", // Input string, required + fixedURI: "", // Expected fixedURI + alternateURI: "", // Expected alternateURI + keywordLookup: false, // Whether a keyword lookup is expected + protocolChange: false, // Whether a protocol change is expected + inWhitelist: false, // Whether the input host is in the whitelist + affectedByDNSForSingleWordHosts: false, // Whether the input host could be a host, but is normally assumed to be a keyword query + } +*/ +var testcases = [ + { + input: "about:home", + fixedURI: "about:home", + }, + { + input: "http://www.mozilla.org", + fixedURI: "http://www.mozilla.org/", + }, + { + input: "http://127.0.0.1/", + fixedURI: "http://127.0.0.1/", + }, + { + input: "file:///foo/bar", + fixedURI: "file:///foo/bar", + }, + { + input: "://www.mozilla.org", + fixedURI: "http://www.mozilla.org/", + protocolChange: true, + }, + { + input: "www.mozilla.org", + fixedURI: "http://www.mozilla.org/", + protocolChange: true, + }, + { + input: "http://mozilla/", + fixedURI: "http://mozilla/", + alternateURI: "https://www.mozilla.com/", + }, + { + input: "http://test./", + fixedURI: "http://test./", + alternateURI: "https://www.test./", + }, + { + input: "127.0.0.1", + fixedURI: "http://127.0.0.1/", + protocolChange: true, + }, + { + input: "1.2.3.4/", + fixedURI: "http://1.2.3.4/", + protocolChange: true, + }, + { + input: "1.2.3.4/foo", + fixedURI: "http://1.2.3.4/foo", + protocolChange: true, + }, + { + input: "1.2.3.4:8000", + fixedURI: "http://1.2.3.4:8000/", + protocolChange: true, + }, + { + input: "1.2.3.4:8000/", + fixedURI: "http://1.2.3.4:8000/", + protocolChange: true, + }, + { + input: "1.2.3.4:8000/foo", + fixedURI: "http://1.2.3.4:8000/foo", + protocolChange: true, + }, + { + input: "192.168.10.110", + fixedURI: "http://192.168.10.110/", + protocolChange: true, + }, + { + input: "192.168.10.110/123", + fixedURI: "http://192.168.10.110/123", + protocolChange: true, + }, + { + input: "192.168.10.110/123foo", + fixedURI: "http://192.168.10.110/123foo", + protocolChange: true, + }, + { + input: "192.168.10.110:1234/123", + fixedURI: "http://192.168.10.110:1234/123", + protocolChange: true, + }, + { + input: "192.168.10.110:1234/123foo", + fixedURI: "http://192.168.10.110:1234/123foo", + protocolChange: true, + }, + { + input: "1.2.3", + fixedURI: "http://1.2.0.3/", + protocolChange: true, + }, + { + input: "1.2.3/", + fixedURI: "http://1.2.0.3/", + protocolChange: true, + }, + { + input: "1.2.3/foo", + fixedURI: "http://1.2.0.3/foo", + protocolChange: true, + }, + { + input: "1.2.3/123", + fixedURI: "http://1.2.0.3/123", + protocolChange: true, + }, + { + input: "1.2.3:8000", + fixedURI: "http://1.2.0.3:8000/", + protocolChange: true, + }, + { + input: "1.2.3:8000/", + fixedURI: "http://1.2.0.3:8000/", + protocolChange: true, + }, + { + input: "1.2.3:8000/foo", + fixedURI: "http://1.2.0.3:8000/foo", + protocolChange: true, + }, + { + input: "1.2.3:8000/123", + fixedURI: "http://1.2.0.3:8000/123", + protocolChange: true, + }, + { + input: "http://1.2.3", + fixedURI: "http://1.2.0.3/", + }, + { + input: "http://1.2.3/", + fixedURI: "http://1.2.0.3/", + }, + { + input: "http://1.2.3/foo", + fixedURI: "http://1.2.0.3/foo", + }, + { + input: "[::1]", + fixedURI: "http://[::1]/", + protocolChange: true, + }, + { + input: "[::1]/", + fixedURI: "http://[::1]/", + protocolChange: true, + }, + { + input: "[::1]:8000", + fixedURI: "http://[::1]:8000/", + protocolChange: true, + }, + { + input: "[::1]:8000/", + fixedURI: "http://[::1]:8000/", + protocolChange: true, + }, + { + input: "[[::1]]/", + keywordLookup: true, + }, + { + input: "[fe80:cd00:0:cde:1257:0:211e:729c]", + fixedURI: "http://[fe80:cd00:0:cde:1257:0:211e:729c]/", + protocolChange: true, + }, + { + input: "[64:ff9b::8.8.8.8]", + fixedURI: "http://[64:ff9b::808:808]/", + protocolChange: true, + }, + { + input: "[64:ff9b::8.8.8.8]/~moz", + fixedURI: "http://[64:ff9b::808:808]/~moz", + protocolChange: true, + }, + { + input: "[::1][::1]", + keywordLookup: true, + }, + { + input: "[::1][100", + keywordLookup: true, + }, + { + input: "[::1]]", + keywordLookup: true, + }, + { + input: "1234", + fixedURI: "http://0.0.4.210/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "whitelisted/foo.txt", + fixedURI: "http://whitelisted/foo.txt", + alternateURI: "https://www.whitelisted.com/foo.txt", + protocolChange: true, + }, + { + input: "mozilla", + fixedURI: "http://mozilla/", + alternateURI: "https://www.mozilla.com/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "test.", + fixedURI: "http://test./", + alternateURI: "https://www.test./", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: ".test", + fixedURI: "http://.test/", + alternateURI: "https://www.test/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "mozilla is amazing", + keywordLookup: true, + }, + { + input: "search ?mozilla", + keywordLookup: true, + }, + { + input: "mozilla .com", + keywordLookup: true, + }, + { + input: "what if firefox?", + keywordLookup: true, + }, + { + input: "london's map", + keywordLookup: true, + }, + { + input: "mozilla ", + fixedURI: "http://mozilla/", + alternateURI: "https://www.mozilla.com/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: " mozilla ", + fixedURI: "http://mozilla/", + alternateURI: "https://www.mozilla.com/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "mozilla \\", + keywordLookup: true, + }, + { + input: "mozilla \\ foo.txt", + keywordLookup: true, + }, + { + input: "mozilla \\\r foo.txt", + keywordLookup: true, + }, + { + input: "mozilla\n", + fixedURI: "http://mozilla/", + alternateURI: "https://www.mozilla.com/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "mozilla \r\n", + fixedURI: "http://mozilla/", + alternateURI: "https://www.mozilla.com/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "moz\r\nfirefox\nos\r", + fixedURI: "http://mozfirefoxos/", + alternateURI: "https://www.mozfirefoxos.com/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "moz\r\n firefox\n", + keywordLookup: true, + }, + { + input: "", + keywordLookup: true, + }, + { + input: "[]", + keywordLookup: true, + }, + { + input: "http://whitelisted/", + fixedURI: "http://whitelisted/", + alternateURI: "https://www.whitelisted.com/", + inWhitelist: true, + }, + { + input: "whitelisted", + fixedURI: "http://whitelisted/", + alternateURI: "https://www.whitelisted.com/", + protocolChange: true, + inWhitelist: true, + }, + { + input: "whitelisted.", + fixedURI: "http://whitelisted./", + alternateURI: "https://www.whitelisted./", + protocolChange: true, + inWhitelist: true, + }, + { + input: "mochi.test", + fixedURI: "http://mochi.test/", + alternateURI: "https://www.mochi.test/", + protocolChange: true, + inWhitelist: true, + }, + // local.domain is a whitelisted suffix... + { + input: "some.local.domain", + fixedURI: "http://some.local.domain/", + protocolChange: true, + inWhitelist: true, + }, + // ...but .domain is not. + { + input: "some.domain", + fixedURI: "http://some.domain/", + alternateURI: "https://www.some.domain/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "café.com", + fixedURI: "http://xn--caf-dma.com/", + alternateURI: "https://www.xn--caf-dma.com/", + protocolChange: true, + }, + { + input: "mozilla.nonexistent", + fixedURI: "http://mozilla.nonexistent/", + alternateURI: "https://www.mozilla.nonexistent/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "mochi.ocm", + fixedURI: "http://mochi.com/", + alternateURI: "https://www.mochi.com/", + protocolChange: true, + }, + { + input: "47.6182,-122.830", + fixedURI: "http://47.6182,-122.830/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "-47.6182,-23.51", + fixedURI: "http://-47.6182,-23.51/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "-22.14,23.51-", + fixedURI: "http://-22.14,23.51-/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "32.7", + fixedURI: "http://32.0.0.7/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "5+2", + fixedURI: "http://5+2/", + alternateURI: "https://www.5+2.com/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "5/2", + fixedURI: "http://0.0.0.5/2", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "moz ?.::%27", + keywordLookup: true, + }, + { + input: "mozilla.com/?q=search", + fixedURI: "http://mozilla.com/?q=search", + alternateURI: "https://www.mozilla.com/?q=search", + protocolChange: true, + }, + { + input: "mozilla.com?q=search", + fixedURI: "http://mozilla.com/?q=search", + alternateURI: "https://www.mozilla.com/?q=search", + protocolChange: true, + }, + { + input: "mozilla.com ?q=search", + keywordLookup: true, + }, + { + input: "mozilla.com.?q=search", + fixedURI: "http://mozilla.com./?q=search", + protocolChange: true, + }, + { + input: "mozilla.com'?q=search", + fixedURI: "http://mozilla.com/?q=search", + alternateURI: "https://www.mozilla.com/?q=search", + protocolChange: true, + }, + { + input: "mozilla.com':search", + keywordLookup: true, + }, + { + input: "[mozilla]", + keywordLookup: true, + }, + { + input: "':?", + fixedURI: "http://'/?", + alternateURI: "https://www.'.com/?", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "whitelisted?.com", + fixedURI: "http://whitelisted/?.com", + alternateURI: "https://www.whitelisted.com/?.com", + protocolChange: true, + }, + { + input: "?'.com", + keywordLookup: true, + }, + { + input: ".com", + keywordLookup: true, + affectedByDNSForSingleWordHosts: true, + fixedURI: "http://.com/", + alternateURI: "https://www.com/", + protocolChange: true, + }, + { + input: "' ?.com", + keywordLookup: true, + }, + { + input: "?mozilla", + keywordLookup: true, + }, + { + input: "??mozilla", + keywordLookup: true, + }, + { + input: "mozilla/", + fixedURI: "http://mozilla/", + alternateURI: "https://www.mozilla.com/", + protocolChange: true, + }, + { + input: "mozilla", + fixedURI: "http://mozilla/", + alternateURI: "https://www.mozilla.com/", + protocolChange: true, + keywordLookup: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "mozilla5/2", + fixedURI: "http://mozilla5/2", + alternateURI: "https://www.mozilla5.com/2", + protocolChange: true, + keywordLookup: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "mozilla/foo", + fixedURI: "http://mozilla/foo", + alternateURI: "https://www.mozilla.com/foo", + protocolChange: true, + keywordLookup: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "mozilla\\", + fixedURI: "http://mozilla/", + alternateURI: "https://www.mozilla.com/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "localhost", + fixedURI: "http://localhost/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "http", + fixedURI: "http://http/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "https", + fixedURI: "http://https/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "localhost:8080", + fixedURI: "http://localhost:8080/", + protocolChange: true, + }, + { + input: "plonk:8080", + fixedURI: "http://plonk:8080/", + protocolChange: true, + }, + { + input: "plonk:8080?test", + fixedURI: "http://plonk:8080/?test", + protocolChange: true, + }, + { + input: "plonk:8080#test", + fixedURI: "http://plonk:8080/#test", + protocolChange: true, + }, + { + input: "plonk/ #", + fixedURI: "http://plonk/%20#", + alternateURI: "https://www.plonk.com/%20#", + protocolChange: true, + keywordLookup: false, + }, + { + input: "blah.com.", + fixedURI: "http://blah.com./", + protocolChange: true, + }, + { + input: + "\u10E0\u10D4\u10D2\u10D8\u10E1\u10E2\u10E0\u10D0\u10EA\u10D8\u10D0.\u10D2\u10D4", + fixedURI: "http://xn--lodaehvb5cdik4g.xn--node/", + alternateURI: "https://www.xn--lodaehvb5cdik4g.xn--node/", + protocolChange: true, + }, + { + input: " \t mozilla.org/\t \t ", + fixedURI: "http://mozilla.org/", + alternateURI: "https://www.mozilla.org/", + protocolChange: true, + }, + { + input: " moz\ti\tlla.org ", + keywordLookup: true, + }, + { + input: "mozilla/", + fixedURI: "http://mozilla/", + alternateURI: "https://www.mozilla.com/", + protocolChange: true, + }, + { + input: "mozilla/ test /", + fixedURI: "http://mozilla/%20test%20/", + alternateURI: "https://www.mozilla.com/%20test%20/", + protocolChange: true, + }, + { + input: "mozilla /test/", + keywordLookup: true, + }, + { + input: "pserver:8080", + fixedURI: "http://pserver:8080/", + protocolChange: true, + }, + { + input: "http;mozilla", + fixedURI: "http://http;mozilla/", + alternateURI: "https://www.http;mozilla.com/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }, + { + input: "http//mozilla.org", + fixedURI: "http://mozilla.org/", + shouldRunTest: flags => + flags & Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS, + }, + { + input: "http//mozilla.org", + fixedURI: "http://http//mozilla.org", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + shouldRunTest: flags => + !(flags & Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS), + }, + { + input: "www.mozilla", + fixedURI: "http://www.mozilla/", + protocolChange: true, + }, + { + input: "https://sub.www..mozilla...org/", + fixedURI: "https://sub.www.mozilla.org/", + }, + { + input: "sub.www..mozilla...org/", + fixedURI: "http://sub.www.mozilla.org/", + protocolChange: true, + }, + { + input: "sub.www..mozilla...org", + fixedURI: "http://sub.www.mozilla.org/", + protocolChange: true, + }, + { + input: "www...mozilla", + fixedURI: "http://www.mozilla/", + keywordLookup: true, + protocolChange: true, + shouldRunTest: flags => + !gSingleWordDNSLookup && + flags & Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP, + }, + { + input: "www...mozilla", + fixedURI: "http://www.mozilla/", + protocolChange: true, + shouldRunTest: flags => + gSingleWordDNSLookup || + !(flags & Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP), + }, + { + input: "mozilla...org", + fixedURI: "http://mozilla.org/", + protocolChange: true, + }, + { + input: "モジラ...org", + fixedURI: "http://xn--yck6dwa.org/", + protocolChange: true, + }, + { + input: "user@localhost", + fixedURI: "http://user@localhost/", + protocolChange: true, + shouldRunTest: () => gSingleWordDNSLookup, + }, + { + input: "user@localhost", + fixedURI: "http://user@localhost/", + keywordLookup: true, + protocolChange: true, + shouldRunTest: () => !gSingleWordDNSLookup, + }, + { + input: "user@192.168.0.1", + fixedURI: "http://user@192.168.0.1/", + protocolChange: true, + }, + { + input: "user@dummy-host", + fixedURI: "http://user@dummy-host/", + protocolChange: true, + shouldRunTest: () => gSingleWordDNSLookup, + }, + { + input: "user@dummy-host", + fixedURI: "http://user@dummy-host/", + keywordLookup: true, + protocolChange: true, + shouldRunTest: () => !gSingleWordDNSLookup, + }, + { + input: "user:pass@dummy-host", + fixedURI: "http://user:pass@dummy-host/", + protocolChange: true, + }, + { + input: ":pass@dummy-host", + fixedURI: "http://:pass@dummy-host/", + protocolChange: true, + }, + { + input: "user@dummy-host/path", + fixedURI: "http://user@dummy-host/path", + protocolChange: true, + shouldRunTest: () => gSingleWordDNSLookup, + }, + { + input: "jar:file:///omni.ja!/", + fixedURI: "jar:file:///omni.ja!/", + }, +]; + +if (AppConstants.platform == "win") { + testcases.push({ + input: "C:\\some\\file.txt", + fixedURI: "file:///C:/some/file.txt", + protocolChange: true, + }); + testcases.push({ + input: "//mozilla", + fixedURI: "http://mozilla/", + alternateURI: "https://www.mozilla.com/", + protocolChange: true, + }); + testcases.push({ + input: "/a", + fixedURI: "http://a/", + alternateURI: "https://www.a.com/", + keywordLookup: true, + protocolChange: true, + affectedByDNSForSingleWordHosts: true, + }); +} else { + testcases.push({ + input: "/some/file.txt", + fixedURI: "file:///some/file.txt", + protocolChange: true, + }); + testcases.push({ + input: "//mozilla", + fixedURI: "file:////mozilla", + protocolChange: true, + }); + testcases.push({ + input: "/a", + fixedURI: "file:///a", + protocolChange: true, + }); +} + +function sanitize(input) { + return input.replace(/\r|\n/g, "").trim(); +} + +add_task(async function setup() { + var prefList = [ + "browser.fixup.typo.scheme", + "keyword.enabled", + "browser.fixup.domainwhitelist.whitelisted", + "browser.fixup.domainsuffixwhitelist.test", + "browser.fixup.domainsuffixwhitelist.local.domain", + "browser.search.separatePrivateDefault", + "browser.search.separatePrivateDefault.ui.enabled", + ]; + for (let pref of prefList) { + Services.prefs.setBoolPref(pref, true); + } + + await setupSearchService(); + await addTestEngines(); + + await Services.search.setDefault( + Services.search.getEngineByName(kSearchEngineID), + Ci.nsISearchService.CHANGE_REASON_UNKNOWN + ); + await Services.search.setDefaultPrivate( + Services.search.getEngineByName(kPrivateSearchEngineID), + Ci.nsISearchService.CHANGE_REASON_UNKNOWN + ); +}); + +var gSingleWordDNSLookup = false; +add_task(async function run_test() { + // Only keywordlookup things should be affected by requiring a DNS lookup for single-word hosts: + info( + "Check only keyword lookup testcases should be affected by requiring DNS for single hosts" + ); + let affectedTests = testcases.filter( + t => !t.keywordLookup && t.affectedByDNSForSingleWordHosts + ); + if (affectedTests.length) { + for (let testcase of affectedTests) { + info("Affected: " + testcase.input); + } + } + Assert.equal(affectedTests.length, 0); + await do_single_test_run(); + await do_single_test_run(true); + gSingleWordDNSLookup = true; + await do_single_test_run(); + await do_single_test_run(true); + gSingleWordDNSLookup = false; + await Services.search.setDefault( + Services.search.getEngineByName(kPostSearchEngineID), + Ci.nsISearchService.CHANGE_REASON_UNKNOWN + ); + await do_single_test_run(); + await do_single_test_run(true); +}); + +async function do_single_test_run(browserFixupAlternateEnabled = false) { + Services.prefs.setBoolPref(kForceDNSLookup, gSingleWordDNSLookup); + Services.prefs.setBoolPref(kFixupEnabled, browserFixupAlternateEnabled); + let relevantTests = gSingleWordDNSLookup + ? testcases.filter(t => t.keywordLookup) + : testcases; + + let engine = await Services.search.getDefault(); + let engineUrl = + engine.name == kPostSearchEngineID + ? kPostSearchEngineURL + : kSearchEngineURL; + let privateEngine = await Services.search.getDefaultPrivate(); + let privateEngineUrl = kPrivateSearchEngineURL; + + for (let { + input: testInput, + fixedURI: expectedFixedURI, + alternateURI: alternativeURI, + keywordLookup: expectKeywordLookup, + protocolChange: expectProtocolChange, + inWhitelist: inWhitelist, + affectedByDNSForSingleWordHosts: affectedByDNSForSingleWordHosts, + shouldRunTest, + } of relevantTests) { + // Explicitly force these into a boolean + expectKeywordLookup = !!expectKeywordLookup; + expectProtocolChange = !!expectProtocolChange; + inWhitelist = !!inWhitelist; + affectedByDNSForSingleWordHosts = !!affectedByDNSForSingleWordHosts; + + expectKeywordLookup = + expectKeywordLookup && + (!affectedByDNSForSingleWordHosts || !gSingleWordDNSLookup); + + for (let flags of flagInputs) { + info( + 'Checking "' + + testInput + + '" with flags ' + + flags + + " (DNS lookup for single words: " + + (gSingleWordDNSLookup ? "yes" : "no") + + "," + + " browser fixup alt enabled: " + + (browserFixupAlternateEnabled ? "yes" : "no") + + ")" + ); + + if (shouldRunTest && !shouldRunTest(flags)) { + continue; + } + + let URIInfo; + try { + URIInfo = Services.uriFixup.getFixupURIInfo(testInput, flags); + } catch (ex) { + // Both APIs should return an error in the same cases. + info("Caught exception: " + ex); + Assert.equal(expectedFixedURI, null); + continue; + } + + // Check the fixedURI: + let fixupFlag = browserFixupAlternateEnabled + ? Services.uriFixup.FIXUP_FLAGS_MAKE_ALTERNATE_URI + : Services.uriFixup.FIXUP_FLAG_NONE; + let forceAlternativeURI = + flags & Services.uriFixup.FIXUP_FLAG_FORCE_ALTERNATE_URI; + let makeAlternativeURI = flags & fixupFlag || forceAlternativeURI; + + if (makeAlternativeURI && alternativeURI != null) { + Assert.equal( + URIInfo.fixedURI.spec, + alternativeURI, + "should have gotten alternate URI" + ); + } else { + Assert.equal( + URIInfo.fixedURI && URIInfo.fixedURI.spec, + expectedFixedURI, + "should get correct fixed URI" + ); + } + + // Check booleans on input: + let couldDoKeywordLookup = + flags & Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP; + Assert.equal( + !!URIInfo.keywordProviderName, + couldDoKeywordLookup && expectKeywordLookup, + "keyword lookup as expected" + ); + + let expectProtocolChangeAfterAlternate = false; + // If alternativeURI was created, the protocol of the URI + // might have been changed to browser.fixup.alternate.protocol + // If the protocol is not the same as what was in expectedFixedURI, + // the protocol must've changed in the fixup process. + if ( + makeAlternativeURI && + alternativeURI != null && + !expectedFixedURI.startsWith(URIInfo.fixedURI.scheme) + ) { + expectProtocolChangeAfterAlternate = true; + } + + Assert.equal( + URIInfo.fixupChangedProtocol, + expectProtocolChange || expectProtocolChangeAfterAlternate, + "protocol change as expected" + ); + Assert.equal( + URIInfo.fixupCreatedAlternateURI, + makeAlternativeURI && alternativeURI != null, + "alternative URI as expected" + ); + + // Check the preferred URI + if (couldDoKeywordLookup) { + if (expectKeywordLookup) { + if (!inWhitelist) { + let urlparamInput = encodeURIComponent(sanitize(testInput)).replace( + /%20/g, + "+" + ); + // If the input starts with `?`, then URIInfo.preferredURI.spec will omit it + // In order to test this behaviour, remove `?` only if it is the first character + if (urlparamInput.startsWith("%3F")) { + urlparamInput = urlparamInput.replace("%3F", ""); + } + let isPrivate = + flags & Services.uriFixup.FIXUP_FLAG_PRIVATE_CONTEXT; + let searchEngineUrl = isPrivate ? privateEngineUrl : engineUrl; + let searchURL = searchEngineUrl.replace( + "{searchTerms}", + urlparamInput + ); + let spec = URIInfo.preferredURI.spec.replace(/%27/g, "'"); + Assert.equal(spec, searchURL, "should get correct search URI"); + let providerName = isPrivate ? privateEngine.name : engine.name; + Assert.equal( + URIInfo.keywordProviderName, + providerName, + "should get correct provider name" + ); + // Also check keywordToURI() uses the right engine. + let kwInfo = Services.uriFixup.keywordToURI( + urlparamInput, + isPrivate + ); + Assert.equal(kwInfo.providerName, URIInfo.providerName); + if (providerName == kPostSearchEngineID) { + Assert.ok(kwInfo.postData); + let submission = engine.getSubmission(urlparamInput); + let enginePostData = NetUtil.readInputStreamToString( + submission.postData, + submission.postData.available() + ); + let postData = NetUtil.readInputStreamToString( + kwInfo.postData, + kwInfo.postData.available() + ); + Assert.equal(postData, enginePostData); + } + } else { + Assert.equal( + URIInfo.preferredURI, + null, + "not expecting a preferred URI" + ); + } + } else { + Assert.equal( + URIInfo.preferredURI.spec, + URIInfo.fixedURI.spec, + "fixed URI should match" + ); + } + } else { + // In these cases, we should never be doing a keyword lookup and + // the fixed URI should be preferred: + let prefURI = URIInfo.preferredURI && URIInfo.preferredURI.spec; + let fixedURI = URIInfo.fixedURI && URIInfo.fixedURI.spec; + Assert.equal(prefURI, fixedURI, "fixed URI should be same as expected"); + } + Assert.equal( + sanitize(testInput), + URIInfo.originalInput, + "should mirror original input" + ); + } + } +} diff --git a/docshell/test/unit/test_URIFixup_search.js b/docshell/test/unit/test_URIFixup_search.js new file mode 100644 index 0000000000..95883302f8 --- /dev/null +++ b/docshell/test/unit/test_URIFixup_search.js @@ -0,0 +1,144 @@ +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); + +var isWin = AppConstants.platform == "win"; + +var data = [ + { + // Valid should not be changed. + wrong: "https://example.com/this/is/a/test.html", + fixed: "https://example.com/this/is/a/test.html", + }, + { + // Unrecognized protocols should be changed. + wrong: "whatever://this/is/a/test.html", + fixed: kSearchEngineURL.replace( + "{searchTerms}", + encodeURIComponent("whatever://this/is/a/test.html") + ), + }, + + { + // Unrecognized protocols should be changed. + wrong: "whatever://this/is/a/test.html", + fixed: kPrivateSearchEngineURL.replace( + "{searchTerms}", + encodeURIComponent("whatever://this/is/a/test.html") + ), + inPrivateBrowsing: true, + }, + + // The following tests check that when a user:password is present in the URL + // `user:` isn't treated as an unknown protocol thus leaking the user and + // password to the search engine. + { + wrong: "user:pass@example.com/this/is/a/test.html", + fixed: "http://user:pass@example.com/this/is/a/test.html", + }, + { + wrong: "user@example.com:8080/this/is/a/test.html", + fixed: "http://user@example.com:8080/this/is/a/test.html", + }, + { + wrong: "https:pass@example.com/this/is/a/test.html", + fixed: "https://pass@example.com/this/is/a/test.html", + }, + { + wrong: "user:pass@example.com:8080/this/is/a/test.html", + fixed: "http://user:pass@example.com:8080/this/is/a/test.html", + }, + { + wrong: "http:user:pass@example.com:8080/this/is/a/test.html", + fixed: "http://user:pass@example.com:8080/this/is/a/test.html", + }, + { + wrong: "ttp:user:pass@example.com:8080/this/is/a/test.html", + fixed: "http://user:pass@example.com:8080/this/is/a/test.html", + }, + { + wrong: "gobbledygook:user:pass@example.com:8080/this/is/a/test.html", + fixed: + "http://gobbledygook:user%3Apass@example.com:8080/this/is/a/test.html", + }, + { + wrong: "user:@example.com:8080/this/is/a/test.html", + fixed: "http://user@example.com:8080/this/is/a/test.html", + }, + { + wrong: "//user:pass@example.com:8080/this/is/a/test.html", + fixed: + (isWin ? "http:" : "file://") + + "//user:pass@example.com:8080/this/is/a/test.html", + }, + { + wrong: "://user:pass@example.com:8080/this/is/a/test.html", + fixed: "http://user:pass@example.com:8080/this/is/a/test.html", + }, + { + wrong: "localhost:8080/?param=1", + fixed: "http://localhost:8080/?param=1", + }, + { + wrong: "localhost:8080?param=1", + fixed: "http://localhost:8080/?param=1", + }, + { + wrong: "localhost:8080#somewhere", + fixed: "http://localhost:8080/#somewhere", + }, + { + wrong: "whatever://this/is/a@b/test.html", + fixed: kSearchEngineURL.replace( + "{searchTerms}", + encodeURIComponent("whatever://this/is/a@b/test.html") + ), + }, +]; + +var extProtocolSvc = Cc[ + "@mozilla.org/uriloader/external-protocol-service;1" +].getService(Ci.nsIExternalProtocolService); + +if (extProtocolSvc && extProtocolSvc.externalProtocolHandlerExists("mailto")) { + data.push({ + wrong: "mailto:foo@bar.com", + fixed: "mailto:foo@bar.com", + }); +} + +var len = data.length; + +add_task(async function setup() { + await setupSearchService(); + await addTestEngines(); + + Services.prefs.setBoolPref("keyword.enabled", true); + Services.prefs.setBoolPref("browser.search.separatePrivateDefault", true); + Services.prefs.setBoolPref( + "browser.search.separatePrivateDefault.ui.enabled", + true + ); + + await Services.search.setDefault( + Services.search.getEngineByName(kSearchEngineID), + Ci.nsISearchService.CHANGE_REASON_UNKNOWN + ); + await Services.search.setDefaultPrivate( + Services.search.getEngineByName(kPrivateSearchEngineID), + Ci.nsISearchService.CHANGE_REASON_UNKNOWN + ); +}); + +// Make sure we fix what needs fixing +add_task(function test_fix_unknown_schemes() { + for (let i = 0; i < len; ++i) { + let item = data[i]; + let flags = Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS; + if (item.inPrivateBrowsing) { + flags |= Services.uriFixup.FIXUP_FLAG_PRIVATE_CONTEXT; + } + let { preferredURI } = Services.uriFixup.getFixupURIInfo(item.wrong, flags); + Assert.equal(preferredURI.spec, item.fixed); + } +}); diff --git a/docshell/test/unit/test_allowJavascript.js b/docshell/test/unit/test_allowJavascript.js new file mode 100644 index 0000000000..e9d2f60fca --- /dev/null +++ b/docshell/test/unit/test_allowJavascript.js @@ -0,0 +1,269 @@ +"use strict"; + +const { XPCShellContentUtils } = ChromeUtils.importESModule( + "resource://testing-common/XPCShellContentUtils.sys.mjs" +); + +XPCShellContentUtils.init(this); + +const ACTOR = "AllowJavascript"; + +const HTML = String.raw`<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <script type="application/javascript"> + "use strict"; + var gFiredOnload = false; + var gFiredOnclick = false; + </script> +</head> +<body onload="gFiredOnload = true;" onclick="gFiredOnclick = true;"> +</body> +</html>`; + +const server = XPCShellContentUtils.createHttpServer({ + hosts: ["example.com", "example.org"], +}); + +server.registerPathHandler("/", (request, response) => { + response.setHeader("Content-Type", "text/html"); + response.write(HTML); +}); + +const { AllowJavascriptParent } = ChromeUtils.importESModule( + "resource://test/AllowJavascriptParent.sys.mjs" +); + +async function assertScriptsAllowed(bc, expectAllowed, desc) { + let actor = bc.currentWindowGlobal.getActor(ACTOR); + let allowed = await actor.sendQuery("CheckScriptsAllowed"); + equal( + allowed, + expectAllowed, + `Scripts should be ${expectAllowed ? "" : "dis"}allowed for ${desc}` + ); +} + +async function assertLoadFired(bc, expectFired, desc) { + let actor = bc.currentWindowGlobal.getActor(ACTOR); + let fired = await actor.sendQuery("CheckFiredLoadEvent"); + equal( + fired, + expectFired, + `Should ${expectFired ? "" : "not "}have fired load for ${desc}` + ); +} + +function createSubframe(bc, url) { + let actor = bc.currentWindowGlobal.getActor(ACTOR); + return actor.sendQuery("CreateIframe", { url }); +} + +add_task(async function() { + Services.prefs.setBoolPref("dom.security.https_first", false); + ChromeUtils.registerWindowActor(ACTOR, { + allFrames: true, + child: { + esModuleURI: "resource://test/AllowJavascriptChild.sys.mjs", + events: { load: { capture: true } }, + }, + parent: { + esModuleURI: "resource://test/AllowJavascriptParent.sys.mjs", + }, + }); + + let page = await XPCShellContentUtils.loadContentPage("http://example.com/", { + remote: true, + remoteSubframes: true, + }); + + let bc = page.browsingContext; + + { + let oopFrame1 = await createSubframe(bc, "http://example.org/"); + let inprocFrame1 = await createSubframe(bc, "http://example.com/"); + + let oopFrame1OopSub = await createSubframe( + oopFrame1, + "http://example.com/" + ); + let inprocFrame1OopSub = await createSubframe( + inprocFrame1, + "http://example.org/" + ); + + equal( + oopFrame1.allowJavascript, + true, + "OOP BC should inherit allowJavascript from parent" + ); + equal( + inprocFrame1.allowJavascript, + true, + "In-process BC should inherit allowJavascript from parent" + ); + equal( + oopFrame1OopSub.allowJavascript, + true, + "OOP BC child should inherit allowJavascript from parent" + ); + equal( + inprocFrame1OopSub.allowJavascript, + true, + "In-process child BC should inherit allowJavascript from parent" + ); + + await assertLoadFired(bc, true, "top BC"); + await assertScriptsAllowed(bc, true, "top BC"); + + await assertLoadFired(oopFrame1, true, "OOP frame 1"); + await assertScriptsAllowed(oopFrame1, true, "OOP frame 1"); + + await assertLoadFired(inprocFrame1, true, "In-process frame 1"); + await assertScriptsAllowed(inprocFrame1, true, "In-process frame 1"); + + await assertLoadFired(oopFrame1OopSub, true, "OOP frame 1 subframe"); + await assertScriptsAllowed(oopFrame1OopSub, true, "OOP frame 1 subframe"); + + await assertLoadFired( + inprocFrame1OopSub, + true, + "In-process frame 1 subframe" + ); + await assertScriptsAllowed( + inprocFrame1OopSub, + true, + "In-process frame 1 subframe" + ); + + bc.allowJavascript = false; + await assertScriptsAllowed(bc, false, "top BC with scripts disallowed"); + await assertScriptsAllowed( + oopFrame1, + false, + "OOP frame 1 with top BC with scripts disallowed" + ); + await assertScriptsAllowed( + inprocFrame1, + false, + "In-process frame 1 with top BC with scripts disallowed" + ); + await assertScriptsAllowed( + oopFrame1OopSub, + false, + "OOP frame 1 subframe with top BC with scripts disallowed" + ); + await assertScriptsAllowed( + inprocFrame1OopSub, + false, + "In-process frame 1 subframe with top BC with scripts disallowed" + ); + + let oopFrame2 = await createSubframe(bc, "http://example.org/"); + let inprocFrame2 = await createSubframe(bc, "http://example.com/"); + + equal( + oopFrame2.allowJavascript, + false, + "OOP BC 2 should inherit allowJavascript from parent" + ); + equal( + inprocFrame2.allowJavascript, + false, + "In-process BC 2 should inherit allowJavascript from parent" + ); + + await assertLoadFired( + oopFrame2, + undefined, + "OOP frame 2 with top BC with scripts disallowed" + ); + await assertScriptsAllowed( + oopFrame2, + false, + "OOP frame 2 with top BC with scripts disallowed" + ); + await assertLoadFired( + inprocFrame2, + undefined, + "In-process frame 2 with top BC with scripts disallowed" + ); + await assertScriptsAllowed( + inprocFrame2, + false, + "In-process frame 2 with top BC with scripts disallowed" + ); + + bc.allowJavascript = true; + await assertScriptsAllowed(bc, true, "top BC"); + + await assertScriptsAllowed(oopFrame1, true, "OOP frame 1"); + await assertScriptsAllowed(inprocFrame1, true, "In-process frame 1"); + await assertScriptsAllowed(oopFrame1OopSub, true, "OOP frame 1 subframe"); + await assertScriptsAllowed( + inprocFrame1OopSub, + true, + "In-process frame 1 subframe" + ); + + await assertScriptsAllowed(oopFrame2, false, "OOP frame 2"); + await assertScriptsAllowed(inprocFrame2, false, "In-process frame 2"); + + oopFrame1.currentWindowGlobal.allowJavascript = false; + inprocFrame1.currentWindowGlobal.allowJavascript = false; + + await assertScriptsAllowed( + oopFrame1, + false, + "OOP frame 1 with second level WC scripts disallowed" + ); + await assertScriptsAllowed( + inprocFrame1, + false, + "In-process frame 1 with second level WC scripts disallowed" + ); + await assertScriptsAllowed( + oopFrame1OopSub, + false, + "OOP frame 1 subframe second level WC scripts disallowed" + ); + await assertScriptsAllowed( + inprocFrame1OopSub, + false, + "In-process frame 1 subframe with second level WC scripts disallowed" + ); + + oopFrame1.reload(0); + inprocFrame1.reload(0); + await Promise.all([ + AllowJavascriptParent.promiseLoad(oopFrame1), + AllowJavascriptParent.promiseLoad(inprocFrame1), + ]); + + equal( + oopFrame1.currentWindowGlobal.allowJavascript, + true, + "WindowContext.allowJavascript does not persist after navigation for OOP frame 1" + ); + equal( + inprocFrame1.currentWindowGlobal.allowJavascript, + true, + "WindowContext.allowJavascript does not persist after navigation for in-process frame 1" + ); + + await assertScriptsAllowed(oopFrame1, true, "OOP frame 1"); + await assertScriptsAllowed(inprocFrame1, true, "In-process frame 1"); + } + + bc.allowJavascript = false; + + bc.reload(0); + await AllowJavascriptParent.promiseLoad(bc); + + await assertLoadFired(bc, undefined, "top BC with scripts disabled"); + await assertScriptsAllowed(bc, false, "top BC with scripts disabled"); + + await page.close(); + Services.prefs.clearUserPref("dom.security.https_first"); +}); diff --git a/docshell/test/unit/test_browsing_context_structured_clone.js b/docshell/test/unit/test_browsing_context_structured_clone.js new file mode 100644 index 0000000000..d06f7aecf6 --- /dev/null +++ b/docshell/test/unit/test_browsing_context_structured_clone.js @@ -0,0 +1,68 @@ +"use strict"; + +add_task(async function test_BrowsingContext_structured_clone() { + let browser = Services.appShell.createWindowlessBrowser(false); + + let frame = browser.document.createElement("iframe"); + + await new Promise(r => { + frame.onload = () => r(); + browser.document.body.appendChild(frame); + }); + + let { browsingContext } = frame; + + let sch = new StructuredCloneHolder({ browsingContext }); + + let deserialize = () => sch.deserialize({}, true); + + // Check that decoding a live browsing context produces the correct + // object. + equal( + deserialize().browsingContext, + browsingContext, + "Got correct browsing context from StructuredClone deserialize" + ); + + // Check that decoding a second time still succeeds. + equal( + deserialize().browsingContext, + browsingContext, + "Got correct browsing context from second StructuredClone deserialize" + ); + + // Destroy the browsing context and make sure that the decode fails + // with a DataCloneError. + // + // Making sure the BrowsingContext is actually destroyed by the time + // we do the second decode is a bit tricky. We obviously have clear + // our local references to it, and give the GC a chance to reap them. + // And we also, of course, have to destroy the frame that it belongs + // to, or its frame loader and window global would hold it alive. + // + // Beyond that, we don't *have* to reload or destroy the parent + // document, but we do anyway just to be safe. + // + + frame.remove(); + frame = null; + browsingContext = null; + + browser.document.location.reload(); + browser.close(); + + // We will schedule a precise GC and do both GC and CC a few times, to make + // sure we have completely destroyed the WindowGlobal actors (which keep + // references to their BrowsingContexts) in order + // to allow their (now snow-white) references to be collected. + await schedulePreciseGCAndForceCC(3); + + // OK. We can be fairly confident that the BrowsingContext object + // stored in our structured clone data has been destroyed. Make sure + // that attempting to decode it again leads to the appropriate error. + Assert.throws( + deserialize, + e => e.name === "DataCloneError", + "Should get a DataCloneError when trying to decode a dead BrowsingContext" + ); +}); diff --git a/docshell/test/unit/test_bug442584.js b/docshell/test/unit/test_bug442584.js new file mode 100644 index 0000000000..c109557f50 --- /dev/null +++ b/docshell/test/unit/test_bug442584.js @@ -0,0 +1,35 @@ +var prefetch = Cc["@mozilla.org/prefetch-service;1"].getService( + Ci.nsIPrefetchService +); + +var ReferrerInfo = Components.Constructor( + "@mozilla.org/referrer-info;1", + "nsIReferrerInfo", + "init" +); + +function run_test() { + // Fill up the queue + Services.prefs.setBoolPref("network.prefetch-next", true); + for (var i = 0; i < 5; i++) { + var uri = Services.io.newURI("http://localhost/" + i); + var referrerInfo = new ReferrerInfo(Ci.nsIReferrerInfo.EMPTY, true, uri); + prefetch.prefetchURI(uri, referrerInfo, null, true); + } + + // Make sure the queue has items in it... + Assert.ok(prefetch.hasMoreElements()); + + // Now disable the pref to force the queue to empty... + Services.prefs.setBoolPref("network.prefetch-next", false); + Assert.ok(!prefetch.hasMoreElements()); + + // Now reenable the pref, and add more items to the queue. + Services.prefs.setBoolPref("network.prefetch-next", true); + for (var k = 0; k < 5; k++) { + var uri2 = Services.io.newURI("http://localhost/" + k); + var referrerInfo2 = new ReferrerInfo(Ci.nsIReferrerInfo.EMPTY, true, uri2); + prefetch.prefetchURI(uri2, referrerInfo2, null, true); + } + Assert.ok(prefetch.hasMoreElements()); +} diff --git a/docshell/test/unit/test_pb_notification.js b/docshell/test/unit/test_pb_notification.js new file mode 100644 index 0000000000..51cd3b95ff --- /dev/null +++ b/docshell/test/unit/test_pb_notification.js @@ -0,0 +1,18 @@ +function destroy_transient_docshell() { + let windowlessBrowser = Services.appShell.createWindowlessBrowser(true); + windowlessBrowser.docShell.setOriginAttributes({ privateBrowsingId: 1 }); + windowlessBrowser.close(); + do_test_pending(); + do_timeout(0, Cu.forceGC); +} + +function run_test() { + var obs = { + observe(aSubject, aTopic, aData) { + Assert.equal(aTopic, "last-pb-context-exited"); + do_test_finished(); + }, + }; + Services.obs.addObserver(obs, "last-pb-context-exited"); + destroy_transient_docshell(); +} diff --git a/docshell/test/unit/test_privacy_transition.js b/docshell/test/unit/test_privacy_transition.js new file mode 100644 index 0000000000..ae1bf71284 --- /dev/null +++ b/docshell/test/unit/test_privacy_transition.js @@ -0,0 +1,21 @@ +var gNotifications = 0; + +var observer = { + QueryInterface: ChromeUtils.generateQI([ + "nsIPrivacyTransitionObserver", + "nsISupportsWeakReference", + ]), + + privateModeChanged(enabled) { + gNotifications++; + }, +}; + +function run_test() { + let windowlessBrowser = Services.appShell.createWindowlessBrowser(true); + windowlessBrowser.docShell.addWeakPrivacyTransitionObserver(observer); + windowlessBrowser.docShell.setOriginAttributes({ privateBrowsingId: 1 }); + windowlessBrowser.docShell.setOriginAttributes({ privateBrowsingId: 0 }); + windowlessBrowser.close(); + Assert.equal(gNotifications, 2); +} diff --git a/docshell/test/unit/test_subframe_stop_after_parent_error.js b/docshell/test/unit/test_subframe_stop_after_parent_error.js new file mode 100644 index 0000000000..4ba80c14cd --- /dev/null +++ b/docshell/test/unit/test_subframe_stop_after_parent_error.js @@ -0,0 +1,141 @@ +"use strict"; +// Tests that pending subframe requests for an initial about:blank +// document do not delay showing load errors (and possibly result in a +// crash at docShell destruction) for failed document loads. + +const { XPCShellContentUtils } = ChromeUtils.importESModule( + "resource://testing-common/XPCShellContentUtils.sys.mjs" +); + +XPCShellContentUtils.init(this); + +const server = XPCShellContentUtils.createHttpServer({ + hosts: ["example.com"], +}); + +// Registers a URL with the HTTP server which will not return a response +// until we're ready to. +function registerSlowPage(path) { + let result = { + url: `http://example.com/${path}`, + }; + + let finishedPromise = new Promise(resolve => { + result.finish = resolve; + }); + + server.registerPathHandler(`/${path}`, async (request, response) => { + response.processAsync(); + + response.setHeader("Content-Type", "text/html"); + response.write("<html><body>Hello.</body></html>"); + + await finishedPromise; + + response.finish(); + }); + + return result; +} + +let topFrameRequest = registerSlowPage("top.html"); +let subFrameRequest = registerSlowPage("frame.html"); + +let thunks = new Set(); +function promiseStateStop(webProgress) { + return new Promise(resolve => { + let listener = { + onStateChange(aWebProgress, request, stateFlags, status) { + if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) { + webProgress.removeProgressListener(listener); + + thunks.delete(listener); + resolve(); + } + }, + + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), + }; + + // Keep the listener alive, since it's stored as a weak reference. + thunks.add(listener); + webProgress.addProgressListener( + listener, + Ci.nsIWebProgress.NOTIFY_STATE_REQUEST + ); + }); +} + +async function runTest(waitForErrorPage) { + let page = await XPCShellContentUtils.loadContentPage("about:blank", { + remote: false, + }); + let { browser } = page; + + // Watch for the HTTP request for the top frame so that we can cancel + // it with an error. + let requestPromise = TestUtils.topicObserved( + "http-on-modify-request", + subject => subject.QueryInterface(Ci.nsIRequest).name == topFrameRequest.url + ); + + // Create a frame with a source URL which will not return a response + // before we cancel it with an error. + let doc = browser.contentDocument; + let frame = doc.createElement("iframe"); + frame.src = topFrameRequest.url; + doc.body.appendChild(frame); + + // Create a subframe in the initial about:blank document for the above + // frame which will not return a response before we cancel the + // document request. + let frameDoc = frame.contentDocument; + let subframe = frameDoc.createElement("iframe"); + subframe.src = subFrameRequest.url; + frameDoc.body.appendChild(subframe); + + let [req] = await requestPromise; + + info("Cancel request for parent frame"); + req.cancel(Cr.NS_ERROR_PROXY_CONNECTION_REFUSED); + + // Request cancelation is not synchronous, so wait for the STATE_STOP + // event to fire. + await promiseStateStop( + browser.docShell.nsIInterfaceRequestor.getInterface(Ci.nsIWebProgress) + ); + + // And make a trip through the event loop to give the DocLoader a + // chance to update its state. + await new Promise(executeSoon); + + if (waitForErrorPage) { + // Make sure that canceling the request with an error code actually + // shows an error page without waiting for a subframe response. + await TestUtils.waitForCondition(() => + frame.contentDocument.documentURI.startsWith("about:neterror?") + ); + } + + info("Remove frame"); + frame.remove(); + + await page.close(); +} + +add_task(async function testRemoveFrameImmediately() { + await runTest(false); +}); + +add_task(async function testRemoveFrameAfterErrorPage() { + await runTest(true); +}); + +add_task(async function() { + // Allow the document requests for the frames to complete. + topFrameRequest.finish(); + subFrameRequest.finish(); +}); diff --git a/docshell/test/unit/xpcshell.ini b/docshell/test/unit/xpcshell.ini new file mode 100644 index 0000000000..8bd7ef4f28 --- /dev/null +++ b/docshell/test/unit/xpcshell.ini @@ -0,0 +1,35 @@ +[DEFAULT] +head = head_docshell.js +support-files = + data/engine.xml + data/enginePost.xml + data/enginePrivate.xml + +[test_allowJavascript.js] +skip-if = os == 'android' +support-files = + AllowJavascriptChild.sys.mjs + AllowJavascriptParent.sys.mjs +[test_bug442584.js] +[test_browsing_context_structured_clone.js] +[test_URIFixup.js] +[test_URIFixup_check_host.js] +[test_URIFixup_external_protocol_fallback.js] +skip-if = os == 'android' +[test_URIFixup_forced.js] +# Disabled for 1563343 -- URI fixup should be done at the app level in GV. +skip-if = os == 'android' +[test_URIFixup_search.js] +skip-if = os == 'android' +[test_URIFixup_info.js] +skip-if = + os == 'android' + win10_2004 && debug # Bug 1727925 +[test_pb_notification.js] +# Bug 751575: unrelated JS changes cause timeouts on random platforms +skip-if = true +[test_privacy_transition.js] +[test_subframe_stop_after_parent_error.js] +skip-if = + os == 'android' + appname == 'thunderbird' # Needs to run without E10s, can't do that. diff --git a/docshell/test/unit_ipc/test_pb_notification_ipc.js b/docshell/test/unit_ipc/test_pb_notification_ipc.js new file mode 100644 index 0000000000..ecb366a3e1 --- /dev/null +++ b/docshell/test/unit_ipc/test_pb_notification_ipc.js @@ -0,0 +1,15 @@ +function run_test() { + var notifications = 0; + var obs = { + observe(aSubject, aTopic, aData) { + Assert.equal(aTopic, "last-pb-context-exited"); + notifications++; + }, + }; + Services.obs.addObserver(obs, "last-pb-context-exited"); + + run_test_in_child("../unit/test_pb_notification.js", function() { + Assert.equal(notifications, 1); + do_test_finished(); + }); +} diff --git a/docshell/test/unit_ipc/xpcshell.ini b/docshell/test/unit_ipc/xpcshell.ini new file mode 100644 index 0000000000..30e98a270f --- /dev/null +++ b/docshell/test/unit_ipc/xpcshell.ini @@ -0,0 +1,7 @@ +[DEFAULT] +head = +skip-if = toolkit == 'android' + +[test_pb_notification_ipc.js] +# Bug 751575: Perma-fails with: command timed out: 1200 seconds without output +skip-if = true |