diff options
Diffstat (limited to 'toolkit/components/windowwatcher/test')
22 files changed, 1221 insertions, 0 deletions
diff --git a/toolkit/components/windowwatcher/test/browser.toml b/toolkit/components/windowwatcher/test/browser.toml new file mode 100644 index 0000000000..90882dc378 --- /dev/null +++ b/toolkit/components/windowwatcher/test/browser.toml @@ -0,0 +1,35 @@ +[DEFAULT] +tags = "openwindow" +support-files = ["head.js"] + +["browser_new_content_window_chromeflags.js"] + +["browser_new_content_window_from_chrome_principal.js"] + +["browser_new_remote_window_flags.js"] + +["browser_new_sized_window.js"] +skip-if = ["os == 'win'"] # Bug 1276802 - Opening windows from content on Windows might not get the size right + +["browser_non_popup_from_popup.js"] + +["browser_popup_condition_current.js"] +skip-if = [ + "debug", # Opening many windows takes a long time on slow builds + "tsan", # Opening many windows takes a long time on slow builds + "socketprocess_networking", +] + +["browser_popup_condition_tab.js"] +skip-if = [ + "debug", # Opening many windows takes a long time on slow builds + "tsan", # Opening many windows takes a long time on slow builds + "socketprocess_networking", +] + +["browser_popup_condition_window.js"] +skip-if = [ + "debug", # Opening many windows takes a long time on slow builds + "tsan", # Opening many windows takes a long time on slow builds + "socketprocess_networking", +] diff --git a/toolkit/components/windowwatcher/test/browser_new_content_window_chromeflags.js b/toolkit/components/windowwatcher/test/browser_new_content_window_chromeflags.js new file mode 100644 index 0000000000..4eff3b46f0 --- /dev/null +++ b/toolkit/components/windowwatcher/test/browser_new_content_window_chromeflags.js @@ -0,0 +1,278 @@ +/** + * Tests that chromeFlags are set properly on windows that are + * being opened from content. + */ + +// The following are not allowed from web content. +// +// <feature string>: { +// flag: <associated nsIWebBrowserChrome flag>, +// defaults_to: <what this feature defaults to normally> +// } +const DISALLOWED = { + location: { + flag: Ci.nsIWebBrowserChrome.CHROME_LOCATIONBAR, + defaults_to: true, + }, + chrome: { + flag: Ci.nsIWebBrowserChrome.CHROME_OPENAS_CHROME, + defaults_to: false, + }, + dialog: { + flag: Ci.nsIWebBrowserChrome.CHROME_OPENAS_DIALOG, + defaults_to: false, + }, + private: { + flag: Ci.nsIWebBrowserChrome.CHROME_PRIVATE_WINDOW, + defaults_to: false, + }, + "non-private": { + flag: Ci.nsIWebBrowserChrome.CHROME_NON_PRIVATE_WINDOW, + defaults_to: false, + }, + // "all": + // checked manually, since this is an aggregate + // flag. + // + // "remote": + // checked manually, since its default value will + // depend on whether or not e10s is enabled by default. + alwaysLowered: { + flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_LOWERED, + defaults_to: false, + }, + "z-lock": { + flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_LOWERED, // Renamed to alwaysLowered + defaults_to: false, + }, + alwaysRaised: { + flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_RAISED, + defaults_to: false, + }, + alwaysOnTop: { + flag: Ci.nsIWebBrowserChrome.CHROME_ALWAYS_ON_TOP, + defaults_to: false, + }, + suppressanimation: { + flag: Ci.nsIWebBrowserChrome.CHROME_SUPPRESS_ANIMATION, + defaults_to: false, + }, + extrachrome: { + flag: Ci.nsIWebBrowserChrome.CHROME_EXTRA, + defaults_to: false, + }, + centerscreen: { + flag: Ci.nsIWebBrowserChrome.CHROME_CENTER_SCREEN, + defaults_to: false, + }, + dependent: { + flag: Ci.nsIWebBrowserChrome.CHROME_DEPENDENT, + defaults_to: false, + }, + modal: { + flag: Ci.nsIWebBrowserChrome.CHROME_MODAL, + defaults_to: false, + }, + titlebar: { + flag: Ci.nsIWebBrowserChrome.CHROME_TITLEBAR, + defaults_to: true, + }, + close: { + flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_CLOSE, + defaults_to: true, + }, + resizable: { + flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_RESIZE, + defaults_to: true, + }, + status: { + flag: Ci.nsIWebBrowserChrome.CHROME_STATUSBAR, + defaults_to: true, + }, +}; + +// This magic value of 2 means that by default, when content tries +// to open a new window, it'll actually open in a new window instead +// of a new tab. +Services.prefs.setIntPref("browser.link.open_newwindow", 2); +registerCleanupFunction(() => { + Services.prefs.clearUserPref("browser.link.open_newwindow"); +}); + +/** + * Given some nsIDOMWindow for a window running in the parent process, + * asynchronously return the nsIWebBrowserChrome chrome flags for the + * associated content window. + * + * @param win (nsIDOMWindow) + * @returns int + */ +function getContentChromeFlags(win) { + let b = win.gBrowser.selectedBrowser; + return SpecialPowers.spawn(b, [], async function () { + // Content scripts provide docShell as a global. + /* global docShell */ + docShell.QueryInterface(Ci.nsIInterfaceRequestor); + try { + // This will throw if we're not a remote browser. + return docShell + .getInterface(Ci.nsIBrowserChild) + .QueryInterface(Ci.nsIWebBrowserChrome).chromeFlags; + } catch (e) { + // This must be a non-remote browser... + return docShell.treeOwner.QueryInterface( + Ci.nsIWebBrowserChrome + ).chromeFlags; + } + }); +} + +/** + * For some chromeFlags, ensures that flags in the DISALLOWED + * group were not modified. + * + * @param chromeFlags (int) + * Some chromeFlags to check. + */ +function assertContentFlags(chromeFlags, isPopup) { + for (let feature in DISALLOWED) { + let flag = DISALLOWED[feature].flag; + Assert.ok(flag, "Expected flag to be a non-zeroish value"); + if (DISALLOWED[feature].defaults_to) { + // The feature is supposed to default to true, so it should + // stay true. + Assert.ok( + chromeFlags & flag, + `Expected feature ${feature} to be unchanged` + ); + } else { + // The feature is supposed to default to false, so it should + // stay false. + Assert.ok( + !(chromeFlags & flag), + `Expected feature ${feature} to be unchanged` + ); + } + } +} + +/** + * Opens a window from content using window.open with the + * features computed from DISALLOWED. The computed feature string attempts to + * flip every feature away from their default. + */ +add_task(async function test_disallowed_flags() { + // Construct a features string that flips all DISALLOWED features + // to not be their defaults. + const DISALLOWED_STRING = Object.keys(DISALLOWED) + .map(feature => { + let toValue = DISALLOWED[feature].defaults_to ? "no" : "yes"; + return `${feature}=${toValue}`; + }) + .join(","); + + const FEATURES = [DISALLOWED_STRING].join(","); + + const SCRIPT_PAGE = `data:text/html,<script>window.open("about:blank", "_blank", "${FEATURES}");</script>`; + const SCRIPT_PAGE_FOR_CHROME_ALL = `data:text/html,<script>window.open("about:blank", "_blank", "all");</script>`; + + let newWinPromise = BrowserTestUtils.waitForNewWindow(); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: SCRIPT_PAGE, + }, + async function (browser) { + let win = await newWinPromise; + let parentChromeFlags = getParentChromeFlags(win); + assertContentFlags(parentChromeFlags); + + if (win.gMultiProcessBrowser) { + Assert.ok( + parentChromeFlags & Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW, + "Should be remote by default" + ); + } else { + Assert.ok( + !(parentChromeFlags & Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW), + "Should not be remote by default" + ); + } + + // Confusingly, chromeFlags also exist in the content process + // as part of the BrowserChild, so we have to check those too. + let contentChromeFlags = await getContentChromeFlags(win); + assertContentFlags(contentChromeFlags); + + Assert.equal( + parentChromeFlags & Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW, + contentChromeFlags & Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW, + "Should have matching remote value in parent and content" + ); + + Assert.equal( + parentChromeFlags & Ci.nsIWebBrowserChrome.CHROME_FISSION_WINDOW, + contentChromeFlags & Ci.nsIWebBrowserChrome.CHROME_FISSION_WINDOW, + "Should have matching fission value in parent and content" + ); + + await BrowserTestUtils.closeWindow(win); + } + ); + + // We check "all" manually, since that's an aggregate flag + // and doesn't fit nicely into the ALLOWED / DISALLOWED scheme + newWinPromise = BrowserTestUtils.waitForNewWindow(); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: SCRIPT_PAGE_FOR_CHROME_ALL, + }, + async function (browser) { + let win = await newWinPromise; + let parentChromeFlags = getParentChromeFlags(win); + Assert.notEqual( + parentChromeFlags & Ci.nsIWebBrowserChrome.CHROME_ALL, + Ci.nsIWebBrowserChrome.CHROME_ALL, + "Should not have been able to set CHROME_ALL" + ); + await BrowserTestUtils.closeWindow(win); + } + ); +}); + +/** + * Opens a window with some chrome flags specified, which should not affect + * scrollbars flag which defaults to true when not disabled explicitly. + */ +add_task(async function test_scrollbars_flag() { + const SCRIPT = 'window.open("about:blank", "_blank", "toolbar=0");'; + const SCRIPT_PAGE = `data:text/html,<script>${SCRIPT}</script>`; + + let newWinPromise = BrowserTestUtils.waitForNewWindow(); + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: SCRIPT_PAGE, + }, + async function (browser) { + let win = await newWinPromise; + + let parentChromeFlags = getParentChromeFlags(win); + Assert.ok( + parentChromeFlags & Ci.nsIWebBrowserChrome.CHROME_SCROLLBARS, + "Should have scrollbars when not disabled explicitly" + ); + + let contentChromeFlags = await getContentChromeFlags(win); + Assert.ok( + contentChromeFlags & Ci.nsIWebBrowserChrome.CHROME_SCROLLBARS, + "Should have scrollbars when not disabled explicitly" + ); + + await BrowserTestUtils.closeWindow(win); + } + ); +}); diff --git a/toolkit/components/windowwatcher/test/browser_new_content_window_from_chrome_principal.js b/toolkit/components/windowwatcher/test/browser_new_content_window_from_chrome_principal.js new file mode 100644 index 0000000000..28c1c23e60 --- /dev/null +++ b/toolkit/components/windowwatcher/test/browser_new_content_window_from_chrome_principal.js @@ -0,0 +1,44 @@ +"use strict"; + +/** + * Tests that if chrome-privileged code calls .open() on an + * unprivileged window, that the principal in the newly + * opened window is appropriately set. + */ +add_task(async function test_chrome_opens_window() { + // This magic value of 2 means that by default, when content tries + // to open a new window, it'll actually open in a new window instead + // of a new tab. + await SpecialPowers.pushPrefEnv({ + set: [["browser.link.open_newwindow", 2]], + }); + + let newWinPromise = BrowserTestUtils.waitForNewWindow({ + url: "https://example.com/", + }); + + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + content.open("https://example.com/", "_blank"); + }); + + let win = await newWinPromise; + let browser = win.gBrowser.selectedBrowser; + Assert.ok( + E10SUtils.isWebRemoteType(browser.remoteType), + "Should have the default content remote type." + ); + + await SpecialPowers.spawn(browser, [], async function () { + Assert.ok( + !content.document.nodePrincipal.isSystemPrincipal, + "We should not have a system principal." + ); + Assert.equal( + content.document.nodePrincipal.origin, + "https://example.com", + "Should have the example.com principal" + ); + }); + + await BrowserTestUtils.closeWindow(win); +}); diff --git a/toolkit/components/windowwatcher/test/browser_new_remote_window_flags.js b/toolkit/components/windowwatcher/test/browser_new_remote_window_flags.js new file mode 100644 index 0000000000..06a15d123c --- /dev/null +++ b/toolkit/components/windowwatcher/test/browser_new_remote_window_flags.js @@ -0,0 +1,85 @@ +/** + * Tests that when a remote browser opens a new window that the + * newly opened window is also remote. + */ + +const ANCHOR_PAGE = `data:text/html,<a href="about:blank" target="_blank">Click me!</a>`; +const SCRIPT_PAGE = `data:text/html,<script>window.open("about:blank", "_blank");</script>`; + +// This magic value of 2 means that by default, when content tries +// to open a new window, it'll actually open in a new window instead +// of a new tab. +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [["browser.link.open_newwindow", 2]], + }); +}); + +function assertFlags(win) { + let docShell = win.docShell; + let loadContext = docShell.QueryInterface(Ci.nsILoadContext); + let chromeFlags = docShell.treeOwner + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIAppWindow).chromeFlags; + Assert.ok( + loadContext.useRemoteTabs, + "Should be using remote tabs on the load context" + ); + Assert.ok( + chromeFlags & Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW, + "Should have the remoteness chrome flag on the window" + ); +} + +/** + * Content can open a window using a target="_blank" link + */ +add_task(async function test_new_remote_window_flags_target_blank() { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: ANCHOR_PAGE, + }, + async function (browser) { + let newWinPromise = BrowserTestUtils.waitForNewWindow(); + await BrowserTestUtils.synthesizeMouseAtCenter("a", {}, browser); + let win = await newWinPromise; + assertFlags(win); + await BrowserTestUtils.closeWindow(win); + } + ); +}); + +/** + * Content can open a window using window.open + */ +add_task(async function test_new_remote_window_flags_window_open() { + let newWinPromise = BrowserTestUtils.waitForNewWindow(); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: SCRIPT_PAGE, + }, + async function (browser) { + let win = await newWinPromise; + assertFlags(win); + await BrowserTestUtils.closeWindow(win); + } + ); +}); + +/** + * Privileged content scripts can also open new windows + * using content.open. + */ +add_task(async function test_new_remote_window_flags_content_open() { + let newWinPromise = BrowserTestUtils.waitForNewWindow(); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + content.open("about:blank", "_blank"); + }); + + let win = await newWinPromise; + assertFlags(win); + await BrowserTestUtils.closeWindow(win); +}); diff --git a/toolkit/components/windowwatcher/test/browser_new_sized_window.js b/toolkit/components/windowwatcher/test/browser_new_sized_window.js new file mode 100644 index 0000000000..0a1e63a8c8 --- /dev/null +++ b/toolkit/components/windowwatcher/test/browser_new_sized_window.js @@ -0,0 +1,67 @@ +"use strict"; + +/** + * Tests that content can open windows at requested dimensions + * of height and width. + */ + +/** + * This utility function does most of the actual testing. We + * construct a feature string suitable for the passed in width + * and height, and then run that script in content to open the + * new window. When the new window comes up, this function tests + * to ensure that the content area of the initial browser is the + * requested dimensions. Finally, we also ensure that we're not + * persisting the position, size or sizemode of the new browser + * window. + */ +function test_dimensions({ width, height }) { + let features = []; + if (width) { + features.push(`width=${width}`); + } + if (height) { + features.push(`height=${height}`); + } + const FEATURE_STR = features.join(","); + const SCRIPT_PAGE = `data:text/html,<script>window.open("about:blank", "_blank", "${FEATURE_STR}");</script>`; + + let newWinPromise = BrowserTestUtils.waitForNewWindow(); + + return BrowserTestUtils.withNewTab( + { + gBrowser, + url: SCRIPT_PAGE, + }, + async function (browser) { + let win = await newWinPromise; + let rect = win.gBrowser.selectedBrowser.getBoundingClientRect(); + + if (width) { + Assert.equal(rect.width, width, "Should have the requested width"); + } + + if (height) { + Assert.equal(rect.height, height, "Should have the requested height"); + } + + let treeOwner = win.docShell.treeOwner; + let persistPosition = {}; + let persistSize = {}; + let persistSizeMode = {}; + treeOwner.getPersistence(persistPosition, persistSize, persistSizeMode); + + Assert.ok(!persistPosition.value, "Should not persist position"); + Assert.ok(!persistSize.value, "Should not persist size"); + Assert.ok(!persistSizeMode.value, "Should not persist size mode"); + + await BrowserTestUtils.closeWindow(win); + } + ); +} + +add_task(async function test_new_sized_window() { + await test_dimensions({ width: 100 }); + await test_dimensions({ height: 150 }); + await test_dimensions({ width: 300, height: 200 }); +}); diff --git a/toolkit/components/windowwatcher/test/browser_non_popup_from_popup.js b/toolkit/components/windowwatcher/test/browser_non_popup_from_popup.js new file mode 100644 index 0000000000..9c905bacc1 --- /dev/null +++ b/toolkit/components/windowwatcher/test/browser_non_popup_from_popup.js @@ -0,0 +1,53 @@ +"use strict"; + +// Opening non-popup from a popup should open a new tab in the most recent +// non-popup window. +add_task(async function test_non_popup_from_popup() { + const BLANK_PAGE = "data:text/html,"; + + // A page opened in a new tab. + const OPEN_PAGE = "data:text/plain,hello"; + + // A page opened in a new popup. + // This opens a new non-popup page with OPEN_PAGE, + // tha should be opened in a new tab in most recent window. + const NON_POPUP_OPENER = btoa( + `data:text/html,<script>window.open('${OPEN_PAGE}', '', '')</script>` + ); + + // A page opened in a new tab. + // This opens a popup with NON_POPUP_OPENER. + const POPUP_OPENER = `data:text/html,<script>window.open(atob("${NON_POPUP_OPENER}"), "", "width=500");</script>`; + + await SpecialPowers.pushPrefEnv({ + set: [["browser.link.open_newwindow", 3]], + }); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: BLANK_PAGE, + }, + async function (browser) { + // Wait for a popup opened by POPUP_OPENER. + const newPopupPromise = BrowserTestUtils.waitForNewWindow(); + + // Wait for a new tab opened by NON_POPUP_OPENER. + const newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, OPEN_PAGE); + + // Open a new tab that opens a popup with NON_POPUP_OPENER. + BrowserTestUtils.startLoadingURIString(gBrowser, POPUP_OPENER); + + let win = await newPopupPromise; + Assert.ok(true, "popup is opened"); + + let tab = await newTabPromise; + Assert.ok(true, "new tab is opened in the recent window"); + + BrowserTestUtils.removeTab(tab); + await BrowserTestUtils.closeWindow(win); + } + ); + + await SpecialPowers.popPrefEnv(); +}); diff --git a/toolkit/components/windowwatcher/test/browser_popup_condition_current.js b/toolkit/components/windowwatcher/test/browser_popup_condition_current.js new file mode 100644 index 0000000000..a033c6738a --- /dev/null +++ b/toolkit/components/windowwatcher/test/browser_popup_condition_current.js @@ -0,0 +1,13 @@ +"use strict"; + +/** + * Opens windows from content using window.open with several features patterns. + */ +add_task(async function test_popup_conditions_current() { + // Non-popup is opened in a current tab. + await SpecialPowers.pushPrefEnv({ + set: [["browser.link.open_newwindow", 1]], + }); + await testPopupPatterns("current"); + await SpecialPowers.popPrefEnv(); +}); diff --git a/toolkit/components/windowwatcher/test/browser_popup_condition_tab.js b/toolkit/components/windowwatcher/test/browser_popup_condition_tab.js new file mode 100644 index 0000000000..36a98fd511 --- /dev/null +++ b/toolkit/components/windowwatcher/test/browser_popup_condition_tab.js @@ -0,0 +1,13 @@ +"use strict"; + +/** + * Opens windows from content using window.open with several features patterns. + */ +add_task(async function test_popup_conditions_tab() { + // Non-popup is opened in a new tab (default behavior). + await SpecialPowers.pushPrefEnv({ + set: [["browser.link.open_newwindow", 3]], + }); + await testPopupPatterns("tab"); + await SpecialPowers.popPrefEnv(); +}); diff --git a/toolkit/components/windowwatcher/test/browser_popup_condition_window.js b/toolkit/components/windowwatcher/test/browser_popup_condition_window.js new file mode 100644 index 0000000000..6f66124a1a --- /dev/null +++ b/toolkit/components/windowwatcher/test/browser_popup_condition_window.js @@ -0,0 +1,13 @@ +"use strict"; + +/** + * Opens windows from content using window.open with several features patterns. + */ +add_task(async function test_popup_conditions_window() { + // Non-popup is opened in a new window. + await SpecialPowers.pushPrefEnv({ + set: [["browser.link.open_newwindow", 2]], + }); + await testPopupPatterns("window"); + await SpecialPowers.popPrefEnv(); +}); diff --git a/toolkit/components/windowwatcher/test/chrome.toml b/toolkit/components/windowwatcher/test/chrome.toml new file mode 100644 index 0000000000..24dd1019d7 --- /dev/null +++ b/toolkit/components/windowwatcher/test/chrome.toml @@ -0,0 +1,10 @@ +[DEFAULT] +tags = "openwindow" + +["test_alwaysOnTop_windows.html"] +skip-if = ["os != 'win'"] + +["test_dialog_arguments.html"] +support-files = ["file_test_dialog.html"] + +["test_modal_windows.html"] diff --git a/toolkit/components/windowwatcher/test/file_named_window.html b/toolkit/components/windowwatcher/test/file_named_window.html new file mode 100644 index 0000000000..c66d89cd34 --- /dev/null +++ b/toolkit/components/windowwatcher/test/file_named_window.html @@ -0,0 +1,5 @@ +<html> +<body> +test_named_window.html new window +</body> +</html> diff --git a/toolkit/components/windowwatcher/test/file_storage_copied.html b/toolkit/components/windowwatcher/test/file_storage_copied.html new file mode 100644 index 0000000000..e32709315a --- /dev/null +++ b/toolkit/components/windowwatcher/test/file_storage_copied.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML> +<html> +<!-- +This page is opened in a new window by test_storage_copied.html. +We need to return the sessionStorage value for the item "test-item", +by way of postMessage. +--> +<head> +<body>Opened!</body> +<script> + window.postMessage(window.sessionStorage.getItem("test-item"), "*"); +</script> +</html> diff --git a/toolkit/components/windowwatcher/test/file_test_dialog.html b/toolkit/components/windowwatcher/test/file_test_dialog.html new file mode 100644 index 0000000000..efb11d0077 --- /dev/null +++ b/toolkit/components/windowwatcher/test/file_test_dialog.html @@ -0,0 +1,14 @@ +<!DOCTYPE HTML> +<html> +<!-- +This page is opened in a new window by test_dialog_arguments. It is +a dialog which expects a Symbol to be passed in the dialog arguments. +Once we load, we call back into the opener with the argument we were +passed. +--> +<head> +<body>Opened!</body> +<script> + window.arguments[1].done(window.arguments[0]); +</script> +</html> diff --git a/toolkit/components/windowwatcher/test/head.js b/toolkit/components/windowwatcher/test/head.js new file mode 100644 index 0000000000..f72344bcd0 --- /dev/null +++ b/toolkit/components/windowwatcher/test/head.js @@ -0,0 +1,207 @@ +/** + * Given some nsIDOMWindow for a window running in the parent + * process, return the nsIWebBrowserChrome chrome flags for + * the associated XUL window. + * + * @param win (nsIDOMWindow) + * Some window in the parent process. + * @returns int + */ +function getParentChromeFlags(win) { + return win.docShell.treeOwner + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIAppWindow).chromeFlags; +} + +const WINDOW_OPEN_FEATURES_PATTERNS = [ + { features: "", popup: false }, + + // If features isn't empty, the following should be true to open tab/window: + // * location or toolbar (defaults to false) + // * menubar (defaults to false) + // * resizable (defaults to true) + // * scrollbars (defaults to false) + // * status (defaults to false) + { features: "location,menubar,resizable,scrollbars,status", popup: false }, + { features: "toolbar,menubar,resizable,scrollbars,status", popup: false }, + + // resizable defaults to true. + { features: "location,menubar,scrollbars,status", popup: false }, + + // The following testcases use "location,menubar,scrollbars,status" + // as the base non-popup case, and test the boundary between popup + // vs non-popup. + + // If either location or toolbar is true, not popup. + { + features: "toolbar,menubar,resizable,scrollbars,status", + popup: false, + }, + { + features: "location,menubar,resizable,scrollbars,status", + popup: false, + }, + + // If both location and toolbar are false, popup. + { features: "menubar,scrollbars,status", popup: true }, + + // If menubar is false, popup. + { features: "location,resizable,scrollbars,status", popup: true }, + + // If resizable is true, not popup. + { + features: "location,menubar,resizable=yes,scrollbars,status", + popup: false, + }, + + // If resizable is false, popup. + { features: "location,menubar,resizable=0,scrollbars,status", popup: true }, + + // If scrollbars is false, popup. + { features: "location,menubar,resizable,status", popup: true }, + + // If status is false, popup. + { features: "location,menubar,resizable,scrollbars", popup: true }, + + // position and size have no effect. + { + features: + "location,menubar,scrollbars,status," + + "left=100,screenX=100,top=100,screenY=100," + + "width=100,innerWidth=100,outerWidth=100," + + "height=100,innerHeight=100,outerHeight=100", + popup: false, + }, + + // Most feature defaults to false if the feature is not empty. + // Specifying only some of them opens a popup. + { features: "location,toolbar,menubar", popup: true }, + { features: "resizable,scrollbars,status", popup: true }, + + // Specifying unknown feature makes the feature not empty. + { features: "someunknownfeature", popup: true }, + + // noopener and noreferrer are removed before testing if feature is empty. + { features: "noopener,noreferrer", popup: false }, +]; + +const WINDOW_CHROME_FLAGS = { + CHROME_WINDOW_BORDERS: true, + CHROME_WINDOW_CLOSE: true, + CHROME_WINDOW_RESIZE: true, + CHROME_LOCATIONBAR: true, + CHROME_STATUSBAR: true, + CHROME_SCROLLBARS: true, + CHROME_TITLEBAR: true, + + CHROME_MENUBAR: true, + CHROME_TOOLBAR: true, + CHROME_PERSONAL_TOOLBAR: true, +}; + +const POPUP_CHROME_FLAGS = { + CHROME_WINDOW_BORDERS: true, + CHROME_WINDOW_CLOSE: true, + CHROME_WINDOW_RESIZE: true, + CHROME_LOCATIONBAR: true, + CHROME_STATUSBAR: true, + CHROME_SCROLLBARS: true, + CHROME_TITLEBAR: true, + + CHROME_MENUBAR: false, + CHROME_TOOLBAR: false, + CHROME_PERSONAL_TOOLBAR: false, +}; + +async function testPopupPatterns(nonPopup) { + for (const { features, popup } of WINDOW_OPEN_FEATURES_PATTERNS) { + const BLANK_PAGE = "data:text/html,"; + const OPEN_PAGE = "data:text/plain,hello"; + const SCRIPT_PAGE = `data:text/html,<script>window.open("${OPEN_PAGE}", "", "${features}");</script>`; + + async function testNewWindow(flags) { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: BLANK_PAGE, + }, + async function (browser) { + const newWinPromise = BrowserTestUtils.waitForNewWindow(); + BrowserTestUtils.startLoadingURIString(gBrowser, SCRIPT_PAGE); + + const win = await newWinPromise; + const parentChromeFlags = getParentChromeFlags(win); + + for (const [name, visible] of Object.entries(flags)) { + if (visible) { + Assert.equal( + !!(parentChromeFlags & Ci.nsIWebBrowserChrome[name]), + true, + `${name} should be present for features "${features}"` + ); + } else { + Assert.equal( + !!(parentChromeFlags & Ci.nsIWebBrowserChrome[name]), + false, + `${name} should not be present for features "${features}"` + ); + } + } + + await BrowserTestUtils.closeWindow(win); + } + ); + } + + async function testNewTab() { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: BLANK_PAGE, + }, + async function (browser) { + const newTabPromise = BrowserTestUtils.waitForNewTab( + gBrowser, + OPEN_PAGE + ); + BrowserTestUtils.startLoadingURIString(gBrowser, SCRIPT_PAGE); + + let tab = await newTabPromise; + BrowserTestUtils.removeTab(tab); + } + ); + } + + async function testCurrentTab() { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: BLANK_PAGE, + }, + async function (browser) { + const pagePromise = BrowserTestUtils.browserLoaded( + browser, + false, + OPEN_PAGE + ); + BrowserTestUtils.startLoadingURIString(gBrowser, SCRIPT_PAGE); + + await pagePromise; + } + ); + } + + if (!popup) { + if (nonPopup == "window") { + await testNewWindow(WINDOW_CHROME_FLAGS); + } else if (nonPopup == "tab") { + await testNewTab(); + } else { + // current tab + await testCurrentTab(); + } + } else { + await testNewWindow(POPUP_CHROME_FLAGS); + } + } +} diff --git a/toolkit/components/windowwatcher/test/mochitest.toml b/toolkit/components/windowwatcher/test/mochitest.toml new file mode 100644 index 0000000000..67b0f80eb8 --- /dev/null +++ b/toolkit/components/windowwatcher/test/mochitest.toml @@ -0,0 +1,11 @@ +[DEFAULT] +tags = "openwindow" +skip-if = ["os == 'android'"] # Fennec doesn't support web content opening new windows (See bug 1277544 for details) + +["test_blank_named_window.html"] + +["test_named_window.html"] +support-files = ["file_named_window.html"] + +["test_storage_copied.html"] +support-files = ["file_storage_copied.html"] diff --git a/toolkit/components/windowwatcher/test/moz.build b/toolkit/components/windowwatcher/test/moz.build new file mode 100644 index 0000000000..0333f1deda --- /dev/null +++ b/toolkit/components/windowwatcher/test/moz.build @@ -0,0 +1,17 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +BROWSER_CHROME_MANIFESTS += [ + "browser.toml", +] + +MOCHITEST_MANIFESTS += [ + "mochitest.toml", +] + +MOCHITEST_CHROME_MANIFESTS += [ + "chrome.toml", +] diff --git a/toolkit/components/windowwatcher/test/test_alwaysOnTop_windows.html b/toolkit/components/windowwatcher/test/test_alwaysOnTop_windows.html new file mode 100644 index 0000000000..05c768c93f --- /dev/null +++ b/toolkit/components/windowwatcher/test/test_alwaysOnTop_windows.html @@ -0,0 +1,86 @@ +<!DOCTYPE HTML> +<html> +<!-- +Tests the alwaysOnTop window feature for the Windows OS. +--> +<head> + <meta charset="utf-8"> + <title>Test an alwaysOnTop window on Windows</title> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + + <script type="application/javascript"> + + const {BrowserTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/BrowserTestUtils.sys.mjs" + ); + const {ctypes} = ChromeUtils.importESModule( + "resource://gre/modules/ctypes.sys.mjs" + ); + + function assertAlwaysOnTop(win, expected) { + let docShell = win.docShell; + let chromeFlags = docShell.treeOwner + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIAppWindow) + .chromeFlags; + let hasFlag = !!(chromeFlags & Ci.nsIWebBrowserChrome.CHROME_ALWAYS_ON_TOP); + is(hasFlag, expected, "Window should have CHROME_ALWAYS_ON_TOP flag."); + + const hWND = Number(win.docShell.treeOwner.nsIBaseWindow.nativeHandle); + const WS_EX_TOPMOST = 0x00000008; + const GWL_EXSTYLE = -20; + + let lib = ctypes.open("user32.dll"); + // On 32-bit systems, the function we need to call is GetWindowLongW. On + // 64-bit systems, the function is GetWindowLongPtrW. Interestingly, + // the MSDN page[1] for GetWindowLongPtrW claims that calling it should work + // on both 32-bit and 64-bit, but that didn't appear to be the case here + // with local testing, hence the conditional. + // + // [1]: https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowlongptrw + let GetWindowFuncSymbol = Services.appinfo.is64Bit ? "GetWindowLongPtrW" + : "GetWindowLongW"; + let GetWindowFunc = lib.declare(GetWindowFuncSymbol, ctypes.winapi_abi, + ctypes.intptr_t, ctypes.uintptr_t, + ctypes.int); + + let styles = GetWindowFunc(hWND, GWL_EXSTYLE); + let isAlwaysOnTop = !!(styles & WS_EX_TOPMOST); + is(isAlwaysOnTop, expected, "Window should be always on top."); + lib.close(); + } + + if (Services.appinfo.OS != "WINNT") { + ok(false, "This test is only designed to run on Windows."); + } else { + add_task(async function() { + let normalWinOpened = BrowserTestUtils.domWindowOpenedAndLoaded(); + window.openDialog("about:blank", + "_blank", "chrome,width=100,height=100,noopener", null); + let normalWin = await normalWinOpened; + ok(normalWin, "Normal window opened"); + assertAlwaysOnTop(normalWin, false); + await BrowserTestUtils.closeWindow(normalWin); + + let alwaysOnTopWinOpened = BrowserTestUtils.domWindowOpenedAndLoaded(); + window.openDialog("about:blank", + "_blank", "chrome,width=100,height=100,alwaysOnTop,noopener", null); + let alwaysOnTopWin = await alwaysOnTopWinOpened; + ok(alwaysOnTopWin, "AlwaysOnTop window opened"); + assertAlwaysOnTop(alwaysOnTopWin, true); + await BrowserTestUtils.closeWindow(alwaysOnTopWin); + }); + } + + </script> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/toolkit/components/windowwatcher/test/test_blank_named_window.html b/toolkit/components/windowwatcher/test/test_blank_named_window.html new file mode 100644 index 0000000000..f8472d216a --- /dev/null +++ b/toolkit/components/windowwatcher/test/test_blank_named_window.html @@ -0,0 +1,40 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that when opening a window with the reserved name _blank that the new +window does not get that name, and that subsequent window openings with that +name result in new windows being opened. +--> +<head> + <meta charset="utf-8"> + <title>Test named windows</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="head.js" type="application/javascript"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + <script type="application/javascript"> + "use strict"; + add_task(async function() { + // This magic value of 2 means that by default, when content tries + // to open a new window, it'll actually open in a new window instead + // of a new tab. + await SpecialPowers.pushPrefEnv({"set": [ + ["browser.link.open_newwindow", 2], + ]}); + + let win1 = window.open("data:text/html,<p>This is window 1 for test_blank_named_window.html</p>", "_blank"); + + let name = SpecialPowers.wrap(win1).docShell.name; + + is(name, "", "Should have no name"); + + let win2 = window.open("data:text/html,<p>This is window 2 for test_blank_named_window.html</p>", "_blank"); + isnot(win1, win2, "Should not have gotten back the same window"); + + win1.close(); + win2.close(); + }); + </script> +</body> +</html> diff --git a/toolkit/components/windowwatcher/test/test_dialog_arguments.html b/toolkit/components/windowwatcher/test/test_dialog_arguments.html new file mode 100644 index 0000000000..6e937c6d01 --- /dev/null +++ b/toolkit/components/windowwatcher/test/test_dialog_arguments.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that arguments can be passed to dialogs. +--> +<head> + <meta charset="utf-8"> + <title>Test a modal window</title> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + + <script type="application/javascript"> + + const TEST_ITEM = Symbol("test-item"); + + function done(returnedItem) { + is(returnedItem, TEST_ITEM, + "Dialog should have received test item"); + win.close(); + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + let win = window.browsingContext.topChromeWindow.openDialog("file_test_dialog.html", "_blank", "width=100,height=100", TEST_ITEM, window); + </script> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/toolkit/components/windowwatcher/test/test_modal_windows.html b/toolkit/components/windowwatcher/test/test_modal_windows.html new file mode 100644 index 0000000000..3c0aa6ebb5 --- /dev/null +++ b/toolkit/components/windowwatcher/test/test_modal_windows.html @@ -0,0 +1,52 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that the parent can open modal windows, and that the modal window +that is opened reports itself as being modal. +--> +<head> + <meta charset="utf-8"> + <title>Test a modal window</title> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> + + <script type="application/javascript"> + + const {BrowserTestUtils} = ChromeUtils.importESModule( + "resource://testing-common/BrowserTestUtils.sys.mjs" + ); + + add_task(async function() { + BrowserTestUtils.domWindowOpened().then((win) => { + let treeOwner = win.docShell.treeOwner; + let chromeFlags = treeOwner.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIAppWindow) + .chromeFlags; + ok(chromeFlags & Ci.nsIWebBrowserChrome.CHROME_MODAL, + "Should have the modal chrome flag"); + + let wbc = treeOwner.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebBrowserChrome); + ok(wbc.isWindowModal(), "Should report as modal"); + + win.close(); + }); + + window.openDialog("data:text/html,<p>This is a modal window for test_modal_windows.html</p>", + "_blank", "modal,noopener", null); + // Since the modal runs a nested event loop, just to be on the safe side, + // we'll wait a tick of the main event loop before resolving the task. + await new Promise(resolve => setTimeout(resolve, 0)); + }); + + </script> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/toolkit/components/windowwatcher/test/test_named_window.html b/toolkit/components/windowwatcher/test/test_named_window.html new file mode 100644 index 0000000000..af3536dd5e --- /dev/null +++ b/toolkit/components/windowwatcher/test/test_named_window.html @@ -0,0 +1,87 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that when content opens a new window with a name, that the +newly opened window actually gets that name, and that subsequent +attempts to open a window with that name will target the same +window. +--> +<head> + <meta charset="utf-8"> + <title>Test named windows</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="head.js" type="application/javascript"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + <a href="#" id="link">Click me</a> + + <script type="application/javascript"> + "use strict"; + + const NAME = "my_window"; + const TARGET_URL = location.href.replace("test_named_window.html", + "file_named_window.html"); + const TARGET_URL_2 = TARGET_URL + "#2"; + const TARGET_URL_3 = TARGET_URL + "#3"; + + /** + * Returns a Promise that resolves once some target has had + * some event dispatched on it. + * + * @param target + * The thing to wait for the event to be dispatched + * through. + * @param eventName + * The name of the event to wait for. + * @returns Promise + */ + function promiseEvent(target, eventName) { + return new Promise(resolve => { + target.addEventListener(eventName, function(e) { + resolve(e); + }, {capture: true, once: true}); + }); + } + + add_task(async function() { + // This magic value of 2 means that by default, when content tries + // to open a new window, it'll actually open in a new window instead + // of a new tab. + await SpecialPowers.pushPrefEnv({"set": [ + ["browser.link.open_newwindow", 2], + ]}); + + let win1 = window.open(TARGET_URL, "my_window"); + await promiseEvent(win1, "load"); + + let name = SpecialPowers.wrap(win1).docShell.name; + + is(name, NAME, "Should have the expected name"); + is(win1.location.href, new URL(TARGET_URL).href, + "Should have loaded target TARGET_URL in the original window"); + + let hashChange = promiseEvent(win1, "hashchange"); + let win2 = window.open(TARGET_URL_2, "my_window"); + await hashChange; + + is(win1, win2, "Should have gotten back the same window"); + is(win1.location.href, new URL(TARGET_URL_2).href, + "Should have re-targeted pre-existing window"); + + hashChange = promiseEvent(win1, "hashchange"); + let link = document.getElementById("link"); + link.setAttribute("target", NAME); + link.setAttribute("href", TARGET_URL_3); + link.click(); + + await hashChange; + + is(win1.location.href, new URL(TARGET_URL_3).href, + "Should have re-targeted pre-existing window"); + + win1.close(); + }); + </script> +</body> +</html> diff --git a/toolkit/components/windowwatcher/test/test_storage_copied.html b/toolkit/components/windowwatcher/test/test_storage_copied.html new file mode 100644 index 0000000000..de05cd6855 --- /dev/null +++ b/toolkit/components/windowwatcher/test/test_storage_copied.html @@ -0,0 +1,43 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test sessionStorage is copied over when a new window opens to the +same domain as the opener. +--> +<head> + <meta charset="utf-8"> + <title>Test storage copied</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="head.js" type="application/javascript"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + <script type="application/javascript"> + "use strict"; + + function waitForMessage(win) { + return new Promise(resolve => { + win.addEventListener("message", function(event) { + resolve(event.data); + }, {once: true}); + }); + } + + add_task(async function() { + const TEST_VALUE = "test-value"; + // This magic value of 2 means that by default, when content tries + // to open a new window, it'll actually open in a new window instead + // of a new tab. + await SpecialPowers.pushPrefEnv({"set": [ + ["browser.link.open_newwindow", 2], + ]}); + + window.sessionStorage.setItem("test-item", TEST_VALUE); + let win = window.open("file_storage_copied.html", "my_window"); + let data = await waitForMessage(win); + is(data, TEST_VALUE, "Should have cloned the test value"); + win.close(); + }); + </script> +</body> +</html> |