diff options
Diffstat (limited to 'docshell/test/browser')
160 files changed, 8158 insertions, 0 deletions
diff --git a/docshell/test/browser/Bug1622420Child.jsm b/docshell/test/browser/Bug1622420Child.jsm new file mode 100644 index 0000000000..69a8f88e09 --- /dev/null +++ b/docshell/test/browser/Bug1622420Child.jsm @@ -0,0 +1,11 @@ +var EXPORTED_SYMBOLS = ["Bug1622420Child"]; + +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.jsm b/docshell/test/browser/Bug422543Child.jsm new file mode 100644 index 0000000000..b46fa589fd --- /dev/null +++ b/docshell/test/browser/Bug422543Child.jsm @@ -0,0 +1,100 @@ +var EXPORTED_SYMBOLS = ["Bug422543Child"]; + +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; + +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..f47f772731 --- /dev/null +++ b/docshell/test/browser/browser.ini @@ -0,0 +1,183 @@ +[DEFAULT] +support-files = + Bug422543Child.jsm + 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-7-child.html + file_bug234628-7-child.html^headers^ + file_bug234628-7.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_bug852909.pdf + file_bug852909.png + file_bug1046022.html + file_bug1206879.html + file_bug1328501.html + file_bug1328501_frame.html + file_bug1328501_framescript.js + file_bug1543077-1-child.html + file_bug1543077-1.html + file_bug1543077-2-child.html + file_bug1543077-2.html + file_bug1543077-3-child.html + file_bug1543077-3.html + file_bug1543077-4-child.html + file_bug1543077-4.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 + +[browser_backforward_userinteraction.js] +support-files = + dummy_iframe_page.html +[browser_backforward_userinteraction_about.js] +[browser_bug1543077-1.js] +[browser_bug1543077-2.js] +[browser_bug1543077-3.js] +[browser_bug1543077-4.js] +[browser_bug1594938.js] +[browser_bug1206879.js] +[browser_bug1309900_crossProcessHistoryNavigation.js] +[browser_bug1328501.js] +[browser_bug1347823.js] +fail-if = sessionHistoryInParent +[browser_bug134911.js] +[browser_bug1415918_beforeunload_options.js] +[browser_bug1622420.js] +support-files = + file_bug1622420.html + Bug1622420Child.jsm +[browser_bug1673702.js] +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_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-7.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] +[browser_bug441169.js] +[browser_bug503832.js] +skip-if = verify +[browser_bug554155.js] +[browser_bug655270.js] +[browser_bug655273.js] +[browser_bug670318.js] +[browser_bug673467.js] +[browser_bug852909.js] +skip-if = (verify && debug && (os == 'win')) +[browser_bug92473.js] +[browser_data_load_inherit_csp.js] +[browser_dataURI_unique_opaque_origin.js] +[browser_fission_maxOrigins.js] +[browser_uriFixupIntegration.js] +[browser_uriFixupAlternateRedirects.js] +support-files = + redirect_to_example.sjs +[browser_loadURI_postdata.js] +[browser_multiple_pushState.js] +[browser_onbeforeunload.js] +skip-if = tsan # Bug 1683730 +[browser_onbeforeunload_navigation.js] +skip-if = (os == 'win' && !debug) # bug 1300351 +[browser_onunload_stop.js] +[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] +[browser_click_link_within_view_source.js] +[browser_browsingContext-01.js] +skip-if = sessionHistoryInParent +[browser_browsingContext-02.js] +[browser_browsingContext-getAllBrowsingContextsInSubtree.js] +[browser_browsingContext-getWindowByName.js] +[browser_browsingContext-embedder.js] +[browser_csp_uir.js] +support-files = + file_csp_uir.html + file_csp_uir_dummy.html +[browser_cross_process_csp_inheritance.js] +skip-if = !e10s # e10s specific test. +[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] +[browser_browsing_context_discarded.js] +[browser_fall_back_to_https.js] +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] diff --git a/docshell/test/browser/browser_backforward_userinteraction.js b/docshell/test/browser/browser_backforward_userinteraction.js new file mode 100644 index 0000000000..a899004d34 --- /dev/null +++ b/docshell/test/browser/browser_backforward_userinteraction.js @@ -0,0 +1,380 @@ +/* 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 using loadURI. +add_task(async function test_loadURI() { + await runTopLevelTest(loadURI); +}); + +// 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..d1d1b24d9e --- /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 loadURI(TEST_PAGE + "?entry=1"); + assertBackForwardState(true, false); + + await loadURI(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_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_browsingContext-01.js b/docshell/test/browser/browser_browsingContext-01.js new file mode 100644 index 0000000000..d1d6f947ef --- /dev/null +++ b/docshell/test/browser/browser_browsingContext-01.js @@ -0,0 +1,205 @@ +/* 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() { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://example.com" + ) + "dummy_page.html", + }, + async function(browser) { + let path = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://example.com" + ); + await SpecialPowers.spawn(browser, [path], async function(path) { + function waitForMessage(command) { + let r; + let p = new Promise(resolve => { + content.window.addEventListener( + "message", + e => resolve({ result: r, event: e }), + { once: true } + ); + }); + r = command(); + return p; + } + + // Open a new window and wait for the message. + let { result: win, event: e1 } = await waitForMessage(_ => + content.window.open(path + "onpageshow_message.html") + ); + + is(e1.data, "pageshow"); + + { + // Create, attach and load an iframe into the window's document. + let frame = win.document.createElement("iframe"); + win.document.body.appendChild(frame); + frame.src = "dummy_page.html"; + await ContentTaskUtils.waitForEvent(frame, "load"); + } + + is(win.frames.length, 1, "Here we should have an iframe"); + + // The frame should have expected browsing context and docshell. + let frameBC = win.frames[0].docShell.browsingContext; + let winDocShell = win.frames[0].docShell; + + // Navigate the window and wait for the message. + let { event: e2 } = await waitForMessage( + _ => (win.location = path + "onload_message.html") + ); + + is(e2.data, "load"); + is(win.frames.length, 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 { event: e3 } = await waitForMessage(_ => win.history.back()); + + is(e3.data, "pageshow"); + is(win.frames.length, 1, "And again there should be an iframe"); + + is(winDocShell, win.frames[0].docShell, "BF cache cached docshell"); + is( + frameBC, + win.frames[0].docShell.browsingContext, + "BF cache cached BC" + ); + is( + frameBC.id, + win.frames[0].docShell.browsingContext.id, + "BF cached BC's have same id" + ); + is( + win.docShell.browsingContext.children[0], + frameBC, + "BF cached BC's should still be a child of its parent" + ); + is( + win.docShell.browsingContext, + frameBC.parent, + "BF cached BC's should still be a connected to its parent" + ); + + win.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..f4be161fba --- /dev/null +++ b/docshell/test/browser/browser_browsingContext-02.js @@ -0,0 +1,231 @@ +/* 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", + "http://example.com" + ); + const BASE2 = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "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..98c285bcc2 --- /dev/null +++ b/docshell/test/browser/browser_browsingContext-getAllBrowsingContextsInSubtree.js @@ -0,0 +1,51 @@ +/* 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(_ => + 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) => + 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..aa41ee49e4 --- /dev/null +++ b/docshell/test/browser/browser_browsingContext-getWindowByName.js @@ -0,0 +1,35 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; +var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +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_browsing_context_attached.js b/docshell/test/browser/browser_browsing_context_attached.js new file mode 100644 index 0000000000..64d299a2d8 --- /dev/null +++ b/docshell/test/browser/browser_browsing_context_attached.js @@ -0,0 +1,139 @@ +"use strict"; + +const TEST_PATH = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://example.com" + ) + "dummy_page.html"; + +const TOPIC = "browsing-context-attached"; + +async function observeAttached(callback) { + let attached = []; + function observer(subject, topic) { + is(topic, TOPIC, "observing correct topic"); + ok(subject instanceof BrowsingContext, "subject to be a BrowsingContext"); + info(`*** bc id: ${subject.id}`); + attached.push(subject); + } + 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.includes(win.browsingContext), + "got notification for window's chrome browsing context" + ); + ok( + attached.includes(win.gBrowser.selectedBrowser.browsingContext), + "got notification 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.includes(window.browsingContext), + "no notification for the current window's chrome browsing context" + ); + ok( + attached.includes(tab.linkedBrowser.browsingContext), + "got notification 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.includes(window.browsingContext), + "no notification for the current window's chrome browsing context" + ); + ok( + !attached.includes(tab.linkedBrowser.browsingContext), + "no notification for toplevel browsing context" + ); + ok( + attached.includes(browsingContext), + "got notification 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.includes(firstContext), + "got notification for initial toplevel browsing context" + ); + + attached = await observeAttached(async () => { + await loadURI(TEST_PATH); + }); + const secondContext = tab.linkedBrowser.browsingContext; + ok( + attached.includes(secondContext), + "got notification for replaced toplevel browsing context" + ); + isnot(secondContext, firstContext, "browsing context to be replaced"); + is( + secondContext.browserId, + firstContext.browserId, + "browserId has been kept" + ); + + attached = await observeAttached(async () => { + await loadURI("about:robots"); + }); + const thirdContext = tab.linkedBrowser.browsingContext; + ok( + attached.includes(thirdContext), + "got notification for replaced toplevel browsing context" + ); + isnot(thirdContext, secondContext, "browsing context to be replaced"); + 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..7c6e1e7b7f --- /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(subject instanceof BrowsingContext, "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..38d7a0614b --- /dev/null +++ b/docshell/test/browser/browser_bug1206879.js @@ -0,0 +1,49 @@ +add_task(async function() { + let url = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content/", + "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..15ee970fbe --- /dev/null +++ b/docshell/test/browser/browser_bug1328501.js @@ -0,0 +1,58 @@ +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]], + }); + 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..551ddca8ab --- /dev/null +++ b/docshell/test/browser/browser_bug1347823.js @@ -0,0 +1,77 @@ +/** + * 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]], + }); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "data:text/html;charset=utf-8,page1" }, + 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,page2"); + 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]], + }); + + await BrowserTestUtils.withNewTab( + { gBrowser, url: "data:text/html;charset=utf-8,page1" }, + 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,page2"); + 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, 3000); + }); + }); + + // 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, "page1"); + }); + } + ); +}); diff --git a/docshell/test/browser/browser_bug134911.js b/docshell/test/browser/browser_bug134911.js new file mode 100644 index 0000000000..c4e2ab42a9 --- /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 */ + BrowserSetForcedCharacterSet("Shift_JIS"); + }); +} + +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..5ce57cbb60 --- /dev/null +++ b/docshell/test/browser/browser_bug1415918_beforeunload_options.js @@ -0,0 +1,151 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://example.com" +); + +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)); +} + +add_task(async function test() { + await SpecialPowers.pushPrefEnv({ + set: [["dom.require_user_interaction_for_beforeunload", false]], + }); + + await SpecialPowers.pushPrefEnv({ + set: [["prompts.contentPromptSubDialog", 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(); + }); + }); + + let allowNavigation; + let promptShown = false; + let promptDismissed = false; + let promptTimeout; + + const DIALOG_TOPIC = "tabmodal-dialog-loaded"; + async function observer(node) { + promptShown = true; + + if (promptTimeout) { + await delay(promptTimeout); + } + + let button = node.querySelector( + `.tabmodalprompt-button${allowNavigation ? 0 : 1}` + ); + button.click(); + promptDismissed = true; + } + Services.obs.addObserver(observer, DIALOG_TOPIC); + + /* + * Check condition where beforeunload handlers request a prompt. + */ + + // Prompt is shown, user clicks OK. + allowNavigation = true; + promptShown = false; + + ok(browser.permitUnload().permitUnload, "permit unload should be true"); + ok(promptShown, "prompt should have been displayed"); + + // Prompt is shown, user clicks CANCEL. + allowNavigation = false; + promptShown = false; + + ok(!browser.permitUnload().permitUnload, "permit unload should be false"); + ok(promptShown, "prompt should have been displayed"); + + // Prompt is not shown, don't permit unload. + promptShown = false; + 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"); + + promptShown = false; + promptDismissed = false; + promptTimeout = 3 * permitUnloadTimeout; + let promise = browser.asyncPermitUnload(); + + let promiseResolved = false; + promise.then(() => { + promiseResolved = true; + }); + + await TestUtils.waitForCondition(() => promptShown); + ok(!promptDismissed, "Should not have dismissed prompt yet"); + 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"); + + let { permitUnload } = await promise; + ok(promptDismissed, "Should have dismissed prompt"); + ok(!permitUnload, "Should not have permitted unload"); + + promptTimeout = null; + + /* + * Check condition where no one requests a prompt. In all cases, + * permitUnload should be true, and all handlers fired. + */ + + allowNavigation = true; + + url += "?1"; + BrowserTestUtils.loadURI(browser, url); + await BrowserTestUtils.browserLoaded(browser, false, url); + + promptShown = false; + 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"); + + await BrowserTestUtils.removeTab(tab); + + Services.obs.removeObserver(observer, DIALOG_TOPIC); +}); diff --git a/docshell/test/browser/browser_bug1543077-1.js b/docshell/test/browser/browser_bug1543077-1.js new file mode 100644 index 0000000000..61507158e8 --- /dev/null +++ b/docshell/test/browser/browser_bug1543077-1.js @@ -0,0 +1,47 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug1543077-1.html", + afterOpen, + "Japanese", + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u0434"), + 131, + "Parent doc should be IBM866 initially" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u0434"), + 87, + "Child doc should be IBM866 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u3042"), + 131, + "Parent doc should decode as EUC-JP subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u3042"), + 87, + "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_bug1543077-2.js b/docshell/test/browser/browser_bug1543077-2.js new file mode 100644 index 0000000000..30736d7c76 --- /dev/null +++ b/docshell/test/browser/browser_bug1543077-2.js @@ -0,0 +1,47 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug1543077-2.html", + afterOpen, + "Japanese", + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u0412"), + 134, + "Parent doc should be IBM866 initially" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u0412"), + 90, + "Child doc should be IBM866 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u3042"), + 134, + "Parent doc should decode as Shift_JIS subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u3042"), + 90, + "Child doc should decode as Shift_JIS subsequently" + ); + + is( + content.document.characterSet, + "Shift_JIS", + "Parent doc should report Shift_JIS subsequently" + ); + is( + content.frames[0].document.characterSet, + "Shift_JIS", + "Child doc should report Shift_JIS subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug1543077-3.js b/docshell/test/browser/browser_bug1543077-3.js new file mode 100644 index 0000000000..fea8c6a365 --- /dev/null +++ b/docshell/test/browser/browser_bug1543077-3.js @@ -0,0 +1,47 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug1543077-3.html", + afterOpen, + "Japanese", + 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_bug1543077-4.js b/docshell/test/browser/browser_bug1543077-4.js new file mode 100644 index 0000000000..ba20352b7f --- /dev/null +++ b/docshell/test/browser/browser_bug1543077-4.js @@ -0,0 +1,47 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug1543077-4.html", + afterOpen, + "Japanese", + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u0434"), + 131, + "Parent doc should be IBM866 initially" + ); + + is( + content.frames[0].document.documentElement.textContent.indexOf("\u0412"), + 90, + "Child doc should be IBM866 initially" + ); +} + +function afterChangeCharset() { + is( + content.document.documentElement.textContent.indexOf("\u3042"), + 131, + "Parent doc should decode as EUC-JP subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u3042"), + 90, + "Child doc should decode as Shift_JIS subsequently" + ); + + is( + content.document.characterSet, + "EUC-JP", + "Parent doc should report EUC-JP subsequently" + ); + is( + content.frames[0].document.characterSet, + "Shift_JIS", + "Child doc should report Shift_JIS 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..df15eba583 --- /dev/null +++ b/docshell/test/browser/browser_bug1622420.js @@ -0,0 +1,30 @@ +const ACTOR = "Bug1622420"; + +add_task(async function test() { + let base = getRootDirectory(gTestPath).slice(0, -1); + ChromeUtils.registerWindowActor(ACTOR, { + allFrames: true, + child: { + moduleURI: `${base}/Bug1622420Child.jsm`, + }, + }); + + registerCleanupFunction(async () => { + gBrowser.removeTab(tab); + + ChromeUtils.unregisterWindowActor(ACTOR); + }); + + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "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..d643a253e9 --- /dev/null +++ b/docshell/test/browser/browser_bug1648464-1.js @@ -0,0 +1,47 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug1648464-1.html", + afterOpen, + "_autodetect_all", + 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..483103ffa2 --- /dev/null +++ b/docshell/test/browser/browser_bug1673702.js @@ -0,0 +1,24 @@ +const DUMMY = + "http://example.org/browser/docshell/test/browser/dummy_page.html"; +const JSON = + "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_bug1688368-1.js b/docshell/test/browser/browser_bug1688368-1.js new file mode 100644 index 0000000000..ac3b8f2a38 --- /dev/null +++ b/docshell/test/browser/browser_bug1688368-1.js @@ -0,0 +1,25 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug1688368-1.sjs", + afterOpen, + "UTF-8", + 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_bug234628-1.js b/docshell/test/browser/browser_bug234628-1.js new file mode 100644 index 0000000000..e26b582ff7 --- /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, + "windows-1251", + 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("\u0402"), + 129, + "Parent doc should decode as windows-1251 subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u0402"), + 85, + "Child doc should decode as windows-1251 subsequently" + ); + + is( + content.document.characterSet, + "windows-1251", + "Parent doc should report windows-1251 subsequently" + ); + is( + content.frames[0].document.characterSet, + "windows-1251", + "Child doc should report windows-1251 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..d507378ed6 --- /dev/null +++ b/docshell/test/browser/browser_bug234628-10.js @@ -0,0 +1,47 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-10.html", + afterOpen, + "windows-1251", + 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("\u0402"), + 151, + "Parent doc should decode as windows-1251 subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 71, + "Child doc should decode as utf-8 subsequently" + ); + + is( + content.document.characterSet, + "windows-1251", + "Parent doc should report windows-1251 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..be71746aad --- /dev/null +++ b/docshell/test/browser/browser_bug234628-11.js @@ -0,0 +1,47 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-11.html", + afterOpen, + "windows-1251", + 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("\u0402"), + 193, + "Parent doc should decode as windows-1251 subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 107, + "Child doc should decode as utf-8 subsequently" + ); + + is( + content.document.characterSet, + "windows-1251", + "Parent doc should report windows-1251 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..bad7a3c442 --- /dev/null +++ b/docshell/test/browser/browser_bug234628-2.js @@ -0,0 +1,51 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-2.html", + afterOpen, + "windows-1251", + 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("\u0402"), + 129, + "Parent doc should decode as windows-1251 subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf( + "\u0432\u201A\u00AC" + ), + 78, + "Child doc should decode as windows-1251 subsequently" + ); + + is( + content.document.characterSet, + "windows-1251", + "Parent doc should report windows-1251 subsequently" + ); + is( + content.frames[0].document.characterSet, + "windows-1251", + "Child doc should report windows-1251 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..218a81e144 --- /dev/null +++ b/docshell/test/browser/browser_bug234628-3.js @@ -0,0 +1,49 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-3.html", + afterOpen, + "windows-1251", + 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("\u0402"), + 118, + "Parent doc should decode as windows-1251 subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf( + "\u0432\u201A\u00AC" + ), + 73, + "Child doc should decode as windows-1251 subsequently" + ); + + is( + content.document.characterSet, + "windows-1251", + "Parent doc should report windows-1251 subsequently" + ); + is( + content.frames[0].document.characterSet, + "windows-1251", + "Child doc should report windows-1251 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..c7c629bf3e --- /dev/null +++ b/docshell/test/browser/browser_bug234628-4.js @@ -0,0 +1,47 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-4.html", + afterOpen, + "windows-1251", + 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("\u0402"), + 132, + "Parent doc should decode as windows-1251 subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 79, + "Child doc should decode as utf-8 subsequently" + ); + + is( + content.document.characterSet, + "windows-1251", + "Parent doc should report windows-1251 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..6c3462b9fc --- /dev/null +++ b/docshell/test/browser/browser_bug234628-5.js @@ -0,0 +1,47 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-5.html", + afterOpen, + "windows-1251", + 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("\u0402"), + 146, + "Parent doc should decode as windows-1251 subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 87, + "Child doc should decode as utf-16 subsequently" + ); + + is( + content.document.characterSet, + "windows-1251", + "Parent doc should report windows-1251 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..8b5994d52d --- /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, + "windows-1251", + 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("\u0402"), + 190, + "Parent doc should decode as windows-1251 subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf("\u20AC"), + 109, + "Child doc should decode as utf-16 subsequently" + ); + + is( + content.document.characterSet, + "windows-1251", + "Parent doc should report windows-1251 subsequently" + ); + is( + content.frames[0].document.characterSet, + "UTF-16BE", + "Child doc should report UTF-16 subsequently" + ); +} diff --git a/docshell/test/browser/browser_bug234628-7.js b/docshell/test/browser/browser_bug234628-7.js new file mode 100644 index 0000000000..10c4b432f9 --- /dev/null +++ b/docshell/test/browser/browser_bug234628-7.js @@ -0,0 +1,49 @@ +function test() { + var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/"; + runCharsetTest( + rootDir + "file_bug234628-7.html", + afterOpen, + "windows-1251", + afterChangeCharset + ); +} + +function afterOpen() { + is( + content.document.documentElement.textContent.indexOf("\u20AC"), + 188, + "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("\u0402"), + 188, + "Parent doc should decode as windows-1251 subsequently" + ); + is( + content.frames[0].document.documentElement.textContent.indexOf( + "\u0432\u201A\u00AC" + ), + 107, + "Child doc should decode as windows-1251 subsequently" + ); + + is( + content.document.characterSet, + "windows-1251", + "Parent doc should report windows-1251 subsequently" + ); + is( + content.frames[0].document.characterSet, + "windows-1251", + "Child doc should report windows-1251 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..1c27619f2e --- /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/"; + runCharsetTest(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..2ba714c09c --- /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/"; + runCharsetTest(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..d6e88887dc --- /dev/null +++ b/docshell/test/browser/browser_bug388121-2.js @@ -0,0 +1,73 @@ +function test() { + waitForExplicitFinish(); + + var w; + var iteration = 1; + const uris = ["", "about:blank"]; + var uri; + var origDoc; + + function testLoad() { + if (w.document == origDoc) { + // Go back to polling + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + setTimeout(testLoad, 10); + return; + } + var prin = w.document.nodePrincipal; + 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").content; + var prin = w.document.nodePrincipal; + 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 { + origDoc = w.document; + // 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..9287de0e46 --- /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.import( + "resource://testing-common/PlacesTestUtils.jsm" +); + +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..7d29a0017b --- /dev/null +++ b/docshell/test/browser/browser_bug422543.js @@ -0,0 +1,251 @@ +/* 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, () => + 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: { + moduleURI: `${base}/Bug422543Child.jsm`, + }, + }); + + 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, () => + 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..56915ac58f --- /dev/null +++ b/docshell/test/browser/browser_bug554155.js @@ -0,0 +1,32 @@ +add_task(async function test() { + await BrowserTestUtils.withNewTab( + { 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..88d662ba0c --- /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.import( + "resource://testing-common/PlacesTestUtils.jsm" +); + +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..b788224f38 --- /dev/null +++ b/docshell/test/browser/browser_bug655273.js @@ -0,0 +1,54 @@ +/* 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( + { 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_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..3d8e8cc6b7 --- /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(() => { + BrowserSetForcedCharacterSet("Shift_JIS"); + }); +} + +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..bb29cafa18 --- /dev/null +++ b/docshell/test/browser/browser_cross_process_csp_inheritance.js @@ -0,0 +1,124 @@ +/* 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", + "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_uir.js b/docshell/test/browser/browser_csp_uir.js new file mode 100644 index 0000000000..b1565162bc --- /dev/null +++ b/docshell/test/browser/browser_csp_uir.js @@ -0,0 +1,87 @@ +"use strict"; + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "http://example.com" +); +const TEST_URI = TEST_PATH + "file_csp_uir.html"; // important to be http: to test upgrade-insecure-requests +const RESULT_URI = + 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..352c9a616b --- /dev/null +++ b/docshell/test/browser/browser_dataURI_unique_opaque_origin.js @@ -0,0 +1,29 @@ +add_task(async function test_dataURI_unique_opaque_origin() { + 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..806803d2b5 --- /dev/null +++ b/docshell/test/browser/browser_data_load_inherit_csp.js @@ -0,0 +1,109 @@ +"use strict"; + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "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_task(async function setup() { + // 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..0d08a18e19 --- /dev/null +++ b/docshell/test/browser/browser_fall_back_to_https.js @@ -0,0 +1,72 @@ +/* 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.import( + "resource://testing-common/UrlbarTestUtils.jsm" +); + +const bug1002724_tests = [ + { + original: "example.com", + 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..c8a0b35193 --- /dev/null +++ b/docshell/test/browser/browser_fission_maxOrigins.js @@ -0,0 +1,209 @@ +/* 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 = [ + "http://example.com/", + "http://example.org/", + "http://example.net/", + "http://example.tw/", + "http://example.cn/", + "http://example.fi/", + "http://example.in/", + "http://example.lk/", + "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], + ], + }); + + // Make sure we actually record telemetry for our disqualifying origin + // count. + BrowserUtils.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); + BrowserUtils._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. + BrowserUtils.min_interval = null; +}); 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..1bed399b3b --- /dev/null +++ b/docshell/test/browser/browser_history_triggeringprincipal_viewsource.js @@ -0,0 +1,92 @@ +"use strict"; + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "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.getElementsByAttribute( + "id", + "context-viewsource" + )[0]; + vSrcItem.click(); + vSrcCtxtMenu.hidePopup(); + 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.getElementsByAttribute("id", "context-back")[0]; + backItem.click(); + backCtxtMenu.hidePopup(); + 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_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..92587b6567 --- /dev/null +++ b/docshell/test/browser/browser_multiple_pushState.js @@ -0,0 +1,23 @@ +add_task(async function test_multiple_pushState() { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: + "http://example.org/browser/docshell/test/browser/file_multiple_pushState.html", + }, + async function(browser) { + 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.js b/docshell/test/browser/browser_onbeforeunload.js new file mode 100644 index 0000000000..014f0537c6 --- /dev/null +++ b/docshell/test/browser/browser_onbeforeunload.js @@ -0,0 +1,326 @@ +"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(4); + +const BASE_URL = "http://mochi.test:8888/browser/docshell/test/browser/"; + +const TEST_PAGE = BASE_URL + "file_onbeforeunload_0.html"; + +const DIALOG_TOPIC = "tabmodal-dialog-loaded"; + +async function withTabModalPromptCount(expected, task) { + 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(allowNavigation) { + return BrowserUtils.promiseObserved(DIALOG_TOPIC).then( + ({ subject: node }) => { + let button = node.querySelector( + `.tabmodalprompt-button${allowNavigation ? 0 : 1}` + ); + button.click(); + } + ); +} + +// 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(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"); + } +} + +add_task(async function() { + await SpecialPowers.pushPrefEnv({ + set: [["dom.require_user_interaction_for_beforeunload", false]], + }); + + SpecialPowers.pushPrefEnv({ + set: [["prompts.contentPromptSubDialog", 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 }; + }); + }); + } + + 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_onbeforeunload_navigation.js b/docshell/test/browser/browser_onbeforeunload_navigation.js new file mode 100644 index 0000000000..d09a751e90 --- /dev/null +++ b/docshell/test/browser/browser_onbeforeunload_navigation.js @@ -0,0 +1,174 @@ +"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>"); + +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; + }); +} + +async function promiseStayOnPagePrompt(acceptNavigation) { + loadStarted = false; + let [dialog] = await TestUtils.topicObserved("tabmodal-dialog-loaded"); + + ok(!loadStarted, "No load should be started"); + + let button = dialog.querySelector( + acceptNavigation ? ".tabmodalprompt-button0" : ".tabmodalprompt-button1" + ); + button.click(); + + // Make a trip through the event loop so that, if anything is going to + // happen after we deny the navigation, it has a chance to happen + // before we return to our caller. + await new Promise(executeSoon); +} + +add_task(async function test() { + await SpecialPowers.pushPrefEnv({ + set: [["dom.require_user_interaction_for_beforeunload", false]], + }); + + SpecialPowers.pushPrefEnv({ + set: [["prompts.contentPromptSubDialog", 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(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_onunload_stop.js b/docshell/test/browser/browser_onunload_stop.js new file mode 100644 index 0000000000..d21966d269 --- /dev/null +++ b/docshell/test/browser/browser_onunload_stop.js @@ -0,0 +1,22 @@ +/* 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 = + "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..abb9330467 --- /dev/null +++ b/docshell/test/browser/browser_search_notification.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +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"; + const kSearchEngineURL = "http://localhost/?search={searchTerms}"; + await Services.search.addEngineWithDetails(kSearchEngineID, { + method: "get", + template: kSearchEngineURL, + }); + + let oldDefaultEngine = await Services.search.getDefault(); + await Services.search.setDefault( + Services.search.getEngineByName(kSearchEngineID) + ); + + let selectedName = (await Services.search.getDefault()).name; + Assert.equal( + selectedName, + kSearchEngineID, + "Check fake search engine is selected" + ); + + registerCleanupFunction(async function() { + if (oldDefaultEngine) { + await Services.search.setDefault(oldDefaultEngine); + } + let engine = Services.search.getEngineByName(kSearchEngineID); + if (engine) { + await Services.search.removeEngine(engine); + } + }); + + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); + gBrowser.selectedTab = tab; + + gURLBar.value = "firefox"; + gURLBar.handleCommand(); + + let [subject, data] = await TestUtils.topicObserved("keyword-search"); + + let engine = Services.search.defaultEngine; + Assert.ok(engine, "Have default search engine."); + Assert.equal(engine, subject, "Notification subject is engine."); + 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..3b3d1ee51d --- /dev/null +++ b/docshell/test/browser/browser_tab_touch_events.js @@ -0,0 +1,70 @@ +/* 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_body); + }); +}); + +async function test_body() { + let bc = content.browsingContext; + + is( + bc.touchEventsOverride, + "none", + "touchEventsOverride flag should be initially set to NONE" + ); + + bc.touchEventsOverride = "disabled"; + 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_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..b4b3efe49f --- /dev/null +++ b/docshell/test/browser/browser_timelineMarkers-frame-02.js @@ -0,0 +1,183 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// 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 > 0, "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 > 0, "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 > 0, "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..2368b36063 --- /dev/null +++ b/docshell/test/browser/browser_timelineMarkers-frame-03.js @@ -0,0 +1,106 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// 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..a05804c5b3 --- /dev/null +++ b/docshell/test/browser/browser_timelineMarkers-frame-04.js @@ -0,0 +1,123 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// 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 > 0, "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 > 0, "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 > 0, "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..b5c245e451 --- /dev/null +++ b/docshell/test/browser/browser_timelineMarkers-frame-05.js @@ -0,0 +1,150 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// 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 > 0, "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 > 0, "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 > 0, "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 > 0, "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_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..ffe58bc754 --- /dev/null +++ b/docshell/test/browser/browser_uriFixupAlternateRedirects.js @@ -0,0 +1,65 @@ +"use strict"; + +const { UrlbarTestUtils } = ChromeUtils.import( + "resource://testing-common/UrlbarTestUtils.jsm" +); + +const REDIRECTURL = + "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..e00c5e0f63 --- /dev/null +++ b/docshell/test/browser/browser_uriFixupIntegration.js @@ -0,0 +1,114 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { UrlbarTestUtils } = ChromeUtils.import( + "resource://testing-common/UrlbarTestUtils.jsm" +); + +const kSearchEngineID = "browser_urifixup_search_engine"; +const kSearchEngineURL = "http://example.com/?search={searchTerms}"; +const kPrivateSearchEngineID = "browser_urifixup_search_engine_private"; +const kPrivateSearchEngineURL = "http://example.com/?private={searchTerms}"; + +add_task(async function setup() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.search.separatePrivateDefault.ui.enabled", true], + ["browser.search.separatePrivateDefault", true], + ], + }); + + let oldCurrentEngine = await Services.search.getDefault(); + let oldPrivateEngine = await Services.search.getDefaultPrivate(); + + // Add new fake search engines. + let newCurrentEngine = await Services.search.addEngineWithDetails( + kSearchEngineID, + { + method: "get", + template: kSearchEngineURL, + } + ); + await Services.search.setDefault(newCurrentEngine); + + let newPrivateEngine = await Services.search.addEngineWithDetails( + kPrivateSearchEngineID, + { + method: "get", + template: kPrivateSearchEngineURL, + } + ); + await Services.search.setDefaultPrivate(newPrivateEngine); + + // Remove the fake engines when done. + registerCleanupFunction(async () => { + if (oldCurrentEngine) { + await Services.search.setDefault(oldCurrentEngine); + } + if (oldPrivateEngine) { + await Services.search.setDefault(oldPrivateEngine); + } + await Services.search.removeEngine(newCurrentEngine); + await Services.search.removeEngine(newPrivateEngine); + }); +}); + +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..e7e404bb06 --- /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", + "http://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..3a3a5836af --- /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", + "http://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_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-1-child.html b/docshell/test/browser/file_bug1543077-1-child.html new file mode 100644 index 0000000000..d244b27717 --- /dev/null +++ b/docshell/test/browser/file_bug1543077-1-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 EUC-JP: </p> +</body> +</html> + diff --git a/docshell/test/browser/file_bug1543077-1.html b/docshell/test/browser/file_bug1543077-1.html new file mode 100644 index 0000000000..4d37ec18fc --- /dev/null +++ b/docshell/test/browser/file_bug1543077-1.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 EUC-JP: </p> + +<iframe src="file_bug1543077-1-child.html"></iframe> + +</body> +</html> + diff --git a/docshell/test/browser/file_bug1543077-2-child.html b/docshell/test/browser/file_bug1543077-2-child.html new file mode 100644 index 0000000000..c3415e97a4 --- /dev/null +++ b/docshell/test/browser/file_bug1543077-2-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 Shift_JIS: </p> +</body> +</html> + diff --git a/docshell/test/browser/file_bug1543077-2.html b/docshell/test/browser/file_bug1543077-2.html new file mode 100644 index 0000000000..c0ad81570f --- /dev/null +++ b/docshell/test/browser/file_bug1543077-2.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 Shift_JIS: </p> + +<iframe src="file_bug1543077-2-child.html"></iframe> + +</body> +</html> + 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_bug1543077-4-child.html b/docshell/test/browser/file_bug1543077-4-child.html new file mode 100644 index 0000000000..c3415e97a4 --- /dev/null +++ b/docshell/test/browser/file_bug1543077-4-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 Shift_JIS: </p> +</body> +</html> + diff --git a/docshell/test/browser/file_bug1543077-4.html b/docshell/test/browser/file_bug1543077-4.html new file mode 100644 index 0000000000..b8feb4cba6 --- /dev/null +++ b/docshell/test/browser/file_bug1543077-4.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 EUC-JP: </p> + +<iframe src="file_bug1543077-4-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..a14ff296f3 --- /dev/null +++ b/docshell/test/browser/file_bug1688368-1.sjs @@ -0,0 +1,32 @@ +"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_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-7-child.html b/docshell/test/browser/file_bug234628-7-child.html new file mode 100644 index 0000000000..c761ace101 --- /dev/null +++ b/docshell/test/browser/file_bug234628-7-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 BOMless UTF-8 with HTTP charset 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-7-child.html^headers^ b/docshell/test/browser/file_bug234628-7-child.html^headers^ new file mode 100644 index 0000000000..2d1c08b9e8 --- /dev/null +++ b/docshell/test/browser/file_bug234628-7-child.html^headers^ @@ -0,0 +1 @@ +Content-Type: text/html; charset=utf-8 diff --git a/docshell/test/browser/file_bug234628-7.html b/docshell/test/browser/file_bug234628-7.html new file mode 100644 index 0000000000..7cb506096d --- /dev/null +++ b/docshell/test/browser/file_bug234628-7.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-8 with HTTP charset in child</title> +</head> +<body> +<h1>meta declaration in parent and BOMless UTF-8 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-7-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_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_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..580a30645e --- /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(10000000).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..c785675682 --- /dev/null +++ b/docshell/test/browser/frame-head.js @@ -0,0 +1,109 @@ +/* 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.import("resource://gre/modules/Timer.jsm"); + +// 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..47a649606f --- /dev/null +++ b/docshell/test/browser/head.js @@ -0,0 +1,253 @@ +/* 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 charset tests. It loads |url| in a new tab, + * runs |check1| in a ContentTask when the page is ready, switches the + * charset to |charset|, and then runs |check2| in a ContentTask when + * the page has finished reloading. + * + * |charset| and |check2| can be omitted, in which case the test + * finishes when |check1| completes. + */ +function runCharsetTest(url, check1, charset, check2) { + waitForExplicitFinish(); + + BrowserTestUtils.openNewForegroundTab(gBrowser, url, true).then(afterOpen); + + function afterOpen() { + if (charset) { + BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then( + afterChangeCharset + ); + + SpecialPowers.spawn(gBrowser.selectedBrowser, [], check1).then(() => { + BrowserSetForcedCharacterSet(charset); + }); + } else { + SpecialPowers.spawn(gBrowser.selectedBrowser, [], check1).then(() => { + gBrowser.removeCurrentTab(); + finish(); + }); + } + } + + function afterChangeCharset() { + SpecialPowers.spawn(gBrowser.selectedBrowser, [], check2).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/onload_message.html b/docshell/test/browser/onload_message.html new file mode 100644 index 0000000000..6c69b710c7 --- /dev/null +++ b/docshell/test/browser/onload_message.html @@ -0,0 +1,15 @@ +<html> + <head> + <meta charset="utf-8"> + <script> + if (opener) { + addEventListener("load", function() { + opener.postMessage("load", "*"); + }); + } + </script> + </head> + <body> + This file posts a message containing "load" to opener 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..1a44a154ec --- /dev/null +++ b/docshell/test/browser/onpageshow_message.html @@ -0,0 +1,13 @@ +<html> + <head> + <meta charset="utf-8"> + <script> + addEventListener("pageshow", function() { + opener.postMessage("pageshow", "*"); + }); + </script> + </head> + <body> + This file posts a message containing "pageshow" to opener on pageshow. + </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..4175a24805 --- /dev/null +++ b/docshell/test/browser/print_postdata.sjs @@ -0,0 +1,22 @@ +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..eef5e49f43 --- /dev/null +++ b/docshell/test/browser/redirect_to_example.sjs @@ -0,0 +1,4 @@ +function handleRequest(request, response) { + response.setStatusLine(request.httpVersion, 302, "Moved Permanently"); + 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> + |