summaryrefslogtreecommitdiffstats
path: root/toolkit/components/windowwatcher/test
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/windowwatcher/test')
-rw-r--r--toolkit/components/windowwatcher/test/browser.toml35
-rw-r--r--toolkit/components/windowwatcher/test/browser_new_content_window_chromeflags.js278
-rw-r--r--toolkit/components/windowwatcher/test/browser_new_content_window_from_chrome_principal.js44
-rw-r--r--toolkit/components/windowwatcher/test/browser_new_remote_window_flags.js85
-rw-r--r--toolkit/components/windowwatcher/test/browser_new_sized_window.js67
-rw-r--r--toolkit/components/windowwatcher/test/browser_non_popup_from_popup.js53
-rw-r--r--toolkit/components/windowwatcher/test/browser_popup_condition_current.js13
-rw-r--r--toolkit/components/windowwatcher/test/browser_popup_condition_tab.js13
-rw-r--r--toolkit/components/windowwatcher/test/browser_popup_condition_window.js13
-rw-r--r--toolkit/components/windowwatcher/test/chrome.toml10
-rw-r--r--toolkit/components/windowwatcher/test/file_named_window.html5
-rw-r--r--toolkit/components/windowwatcher/test/file_storage_copied.html13
-rw-r--r--toolkit/components/windowwatcher/test/file_test_dialog.html14
-rw-r--r--toolkit/components/windowwatcher/test/head.js207
-rw-r--r--toolkit/components/windowwatcher/test/mochitest.toml11
-rw-r--r--toolkit/components/windowwatcher/test/moz.build17
-rw-r--r--toolkit/components/windowwatcher/test/test_alwaysOnTop_windows.html86
-rw-r--r--toolkit/components/windowwatcher/test/test_blank_named_window.html40
-rw-r--r--toolkit/components/windowwatcher/test/test_dialog_arguments.html35
-rw-r--r--toolkit/components/windowwatcher/test/test_modal_windows.html52
-rw-r--r--toolkit/components/windowwatcher/test/test_named_window.html87
-rw-r--r--toolkit/components/windowwatcher/test/test_storage_copied.html43
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>