summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/fullscreen
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /browser/base/content/test/fullscreen
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--browser/base/content/test/fullscreen/FullscreenFrame.sys.mjs103
-rw-r--r--browser/base/content/test/fullscreen/browser.ini31
-rw-r--r--browser/base/content/test/fullscreen/browser_bug1557041.js47
-rw-r--r--browser/base/content/test/fullscreen/browser_bug1620341.js108
-rw-r--r--browser/base/content/test/fullscreen/browser_fullscreen_api_fission.js252
-rw-r--r--browser/base/content/test/fullscreen/browser_fullscreen_context_menu.js142
-rw-r--r--browser/base/content/test/fullscreen/browser_fullscreen_cross_origin.js64
-rw-r--r--browser/base/content/test/fullscreen/browser_fullscreen_enterInUrlbar.js56
-rw-r--r--browser/base/content/test/fullscreen/browser_fullscreen_from_minimize.js82
-rw-r--r--browser/base/content/test/fullscreen/browser_fullscreen_keydown_reservation.js112
-rw-r--r--browser/base/content/test/fullscreen/browser_fullscreen_menus.js72
-rw-r--r--browser/base/content/test/fullscreen/browser_fullscreen_newtab.js55
-rw-r--r--browser/base/content/test/fullscreen/browser_fullscreen_newwindow.js83
-rw-r--r--browser/base/content/test/fullscreen/browser_fullscreen_permissions_prompt.js160
-rw-r--r--browser/base/content/test/fullscreen/browser_fullscreen_warning.js280
-rw-r--r--browser/base/content/test/fullscreen/browser_fullscreen_window_focus.js110
-rw-r--r--browser/base/content/test/fullscreen/browser_fullscreen_window_open.js102
-rw-r--r--browser/base/content/test/fullscreen/fullscreen.html12
-rw-r--r--browser/base/content/test/fullscreen/fullscreen_frame.html9
-rw-r--r--browser/base/content/test/fullscreen/head.js164
-rw-r--r--browser/base/content/test/fullscreen/open_and_focus_helper.html56
21 files changed, 2100 insertions, 0 deletions
diff --git a/browser/base/content/test/fullscreen/FullscreenFrame.sys.mjs b/browser/base/content/test/fullscreen/FullscreenFrame.sys.mjs
new file mode 100644
index 0000000000..9821837b3f
--- /dev/null
+++ b/browser/base/content/test/fullscreen/FullscreenFrame.sys.mjs
@@ -0,0 +1,103 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * test helper JSWindowActors used by the browser_fullscreen_api_fission.js test.
+ */
+
+export class FullscreenFrameChild extends JSWindowActorChild {
+ actorCreated() {
+ this.fullscreen_events = [];
+ }
+
+ changed() {
+ return new Promise(resolve => {
+ this.contentWindow.document.addEventListener(
+ "fullscreenchange",
+ () => resolve(),
+ {
+ once: true,
+ }
+ );
+ });
+ }
+
+ requestFullscreen() {
+ let doc = this.contentWindow.document;
+ let button = doc.createElement("button");
+ doc.body.appendChild(button);
+
+ return new Promise(resolve => {
+ button.onclick = () => {
+ doc.body.requestFullscreen().then(resolve);
+ doc.body.removeChild(button);
+ };
+ button.click();
+ });
+ }
+
+ receiveMessage(msg) {
+ switch (msg.name) {
+ case "WaitForChange":
+ return this.changed();
+ case "ExitFullscreen":
+ return this.contentWindow.document.exitFullscreen();
+ case "RequestFullscreen":
+ this.browsingContext.isActive = true;
+ return Promise.all([this.changed(), this.requestFullscreen()]);
+ case "CreateChild":
+ let child = msg.data;
+ let iframe = this.contentWindow.document.createElement("iframe");
+ iframe.allow = child.allow_fullscreen ? "fullscreen" : "";
+ iframe.name = child.name;
+
+ let loaded = new Promise(resolve => {
+ iframe.addEventListener(
+ "load",
+ () => resolve(iframe.browsingContext),
+ { once: true }
+ );
+ });
+ iframe.src = child.url;
+ this.contentWindow.document.body.appendChild(iframe);
+ return loaded;
+ case "GetEvents":
+ return Promise.resolve(this.fullscreen_events);
+ case "ClearEvents":
+ this.fullscreen_events = [];
+ return Promise.resolve();
+ case "GetFullscreenElement":
+ let document = this.contentWindow.document;
+ let child_iframe = this.contentWindow.document.getElementsByTagName(
+ "iframe"
+ )
+ ? this.contentWindow.document.getElementsByTagName("iframe")[0]
+ : null;
+ switch (document.fullscreenElement) {
+ case null:
+ return Promise.resolve("null");
+ case document:
+ return Promise.resolve("document");
+ case document.body:
+ return Promise.resolve("body");
+ case child_iframe:
+ return Promise.resolve("child_iframe");
+ default:
+ return Promise.resolve("other");
+ }
+ }
+
+ return Promise.reject("Unexpected Message");
+ }
+
+ async handleEvent(event) {
+ switch (event.type) {
+ case "fullscreenchange":
+ this.fullscreen_events.push(true);
+ break;
+ case "fullscreenerror":
+ this.fullscreen_events.push(false);
+ break;
+ }
+ }
+}
diff --git a/browser/base/content/test/fullscreen/browser.ini b/browser/base/content/test/fullscreen/browser.ini
new file mode 100644
index 0000000000..8eecb3f99c
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser.ini
@@ -0,0 +1,31 @@
+[DEFAULT]
+support-files =
+ head.js
+ open_and_focus_helper.html
+
+[browser_bug1557041.js]
+[browser_bug1620341.js]
+support-files = fullscreen.html fullscreen_frame.html
+[browser_fullscreen_api_fission.js]
+https_first_disabled = true
+support-files = fullscreen.html FullscreenFrame.sys.mjs
+[browser_fullscreen_context_menu.js]
+[browser_fullscreen_cross_origin.js]
+support-files = fullscreen.html fullscreen_frame.html
+[browser_fullscreen_enterInUrlbar.js]
+skip-if = (os == 'mac') || (os == 'linux') # Bug 1648649
+[browser_fullscreen_from_minimize.js]
+skip-if = (os == 'linux') || (os == 'win') # Bug 1818795 and Bug 1818796
+[browser_fullscreen_keydown_reservation.js]
+[browser_fullscreen_menus.js]
+[browser_fullscreen_newtab.js]
+[browser_fullscreen_newwindow.js]
+[browser_fullscreen_permissions_prompt.js]
+[browser_fullscreen_warning.js]
+support-files = fullscreen.html
+skip-if =
+ (os == 'mac') # Bug 1848423
+[browser_fullscreen_window_focus.js]
+skip-if = (os == 'mac') && debug # Bug 1568570
+[browser_fullscreen_window_open.js]
+skip-if = (os == 'linux') && swgl # Bug 1795491
diff --git a/browser/base/content/test/fullscreen/browser_bug1557041.js b/browser/base/content/test/fullscreen/browser_bug1557041.js
new file mode 100644
index 0000000000..3f00de86a0
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_bug1557041.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// This test tends to trigger a race in the fullscreen time telemetry,
+// where the fullscreen enter and fullscreen exit events (which use the
+// same histogram ID) overlap. That causes TelemetryStopwatch to log an
+// error.
+SimpleTest.ignoreAllUncaughtExceptions(true);
+
+add_task(async function test_identityPopupCausesFSExit() {
+ let url = "https://example.com/";
+
+ await BrowserTestUtils.withNewTab("about:blank", async browser => {
+ let loaded = BrowserTestUtils.browserLoaded(browser, false, url);
+ BrowserTestUtils.loadURIString(browser, url);
+ await loaded;
+
+ let identityPermissionBox = document.getElementById(
+ "identity-permission-box"
+ );
+
+ info("Entering DOM fullscreen");
+ await changeFullscreen(browser, true);
+
+ let popupShown = BrowserTestUtils.waitForEvent(
+ window,
+ "popupshown",
+ true,
+ event => event.target == document.getElementById("permission-popup")
+ );
+ let fsExit = waitForFullScreenState(browser, false);
+
+ identityPermissionBox.click();
+
+ info("Waiting for fullscreen exit and permission popup to show");
+ await Promise.all([fsExit, popupShown]);
+
+ let identityPopup = document.getElementById("permission-popup");
+ ok(
+ identityPopup.hasAttribute("panelopen"),
+ "Identity popup should be open"
+ );
+ ok(!window.fullScreen, "Should not be in full-screen");
+ });
+});
diff --git a/browser/base/content/test/fullscreen/browser_bug1620341.js b/browser/base/content/test/fullscreen/browser_bug1620341.js
new file mode 100644
index 0000000000..bd836eb5a3
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_bug1620341.js
@@ -0,0 +1,108 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const tab1URL = `data:text/html,
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta charset="utf-8"/>
+ <title>First tab to be loaded</title>
+ </head>
+ <body>
+ <button>JUST A BUTTON</button>
+ </body>
+ </html>`;
+
+const ORIGIN =
+ "https://example.com/browser/browser/base/content/test/fullscreen/fullscreen_frame.html";
+
+add_task(async function test_fullscreen_cross_origin() {
+ async function requestFullscreenThenCloseTab() {
+ await BrowserTestUtils.withNewTab(ORIGIN, async function (browser) {
+ info("Start fullscreen on iframe frameAllowed");
+
+ // Make sure there is no attribute "inDOMFullscreen" before requesting fullscreen.
+ await TestUtils.waitForCondition(
+ () => !document.documentElement.hasAttribute("inDOMFullscreen")
+ );
+
+ let tabbrowser = browser.ownerDocument.querySelector("#tabbrowser-tabs");
+ ok(
+ !tabbrowser.hasAttribute("closebuttons"),
+ "Close buttons should be visible on every tab"
+ );
+
+ // Request fullscreen from iframe
+ await SpecialPowers.spawn(browser, [], async function () {
+ let frame = content.document.getElementById("frameAllowed");
+ frame.focus();
+ await SpecialPowers.spawn(frame, [], async () => {
+ let frameDoc = content.document;
+ const waitForFullscreen = new Promise(resolve => {
+ const message = "fullscreenchange";
+ function handler(evt) {
+ frameDoc.removeEventListener(message, handler);
+ Assert.equal(evt.type, message, `Request should be allowed`);
+ resolve();
+ }
+ frameDoc.addEventListener(message, handler);
+ });
+
+ frameDoc.getElementById("request").click();
+ await waitForFullscreen;
+ });
+ });
+
+ // Make sure there is attribute "inDOMFullscreen" after requesting fullscreen.
+ await TestUtils.waitForCondition(() =>
+ document.documentElement.hasAttribute("inDOMFullscreen")
+ );
+
+ await TestUtils.waitForCondition(
+ () => tabbrowser.hasAttribute("closebuttons"),
+ "Close buttons should be visible only on the active tab (tabs have width=0 so closebuttons gets set on them)"
+ );
+ });
+ }
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["full-screen-api.enabled", true],
+ ["full-screen-api.allow-trusted-requests-only", false],
+ ["full-screen-api.transition-duration.enter", "0 0"],
+ ["full-screen-api.transition-duration.leave", "0 0"],
+ ["dom.security.featurePolicy.header.enabled", true],
+ ["dom.security.featurePolicy.webidl.enabled", true],
+ ],
+ });
+
+ // Open a tab with tab1URL.
+ let tab1 = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ tab1URL,
+ true
+ );
+
+ // 1. Open another tab and load a page with two iframes.
+ // 2. Request fullscreen from an iframe which is in a different origin.
+ // 3. Close the tab after receiving "fullscreenchange" message.
+ // Note that we don't do "doc.exitFullscreen()" before closing the tab
+ // on purpose.
+ await requestFullscreenThenCloseTab();
+
+ // Wait until attribute "inDOMFullscreen" is removed.
+ await TestUtils.waitForCondition(
+ () => !document.documentElement.hasAttribute("inDOMFullscreen")
+ );
+
+ await TestUtils.waitForCondition(
+ () => !gBrowser.tabContainer.hasAttribute("closebuttons"),
+ "Close buttons should come back to every tab"
+ );
+
+ // Remove the remaining tab and leave the test.
+ let tabClosed = BrowserTestUtils.waitForTabClosing(tab1);
+ BrowserTestUtils.removeTab(tab1);
+ await tabClosed;
+});
diff --git a/browser/base/content/test/fullscreen/browser_fullscreen_api_fission.js b/browser/base/content/test/fullscreen/browser_fullscreen_api_fission.js
new file mode 100644
index 0000000000..03b65ddc0e
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_fullscreen_api_fission.js
@@ -0,0 +1,252 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* This test checks that `document.fullscreenElement` is set correctly and
+ * proper fullscreenchange events fire when an element inside of a
+ * multi-origin tree of iframes calls `requestFullscreen()`. It is designed
+ * to make sure the fullscreen API is working properly in fission when the
+ * frame tree spans multiple processes.
+ *
+ * A similarly purposed Web Platform Test exists, but at the time of writing
+ * is manual, so it cannot be run in CI:
+ * `element-request-fullscreen-cross-origin-manual.sub.html`
+ */
+
+"use strict";
+
+const actorModuleURI = getRootDirectory(gTestPath) + "FullscreenFrame.sys.mjs";
+const actorName = "FullscreenFrame";
+
+const fullscreenPath =
+ getRootDirectory(gTestPath).replace("chrome://mochitests/content", "") +
+ "fullscreen.html";
+
+const fullscreenTarget = "D";
+// TOP
+// | \
+// A B
+// |
+// C
+// |
+// D
+// |
+// E
+const frameTree = {
+ name: "TOP",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ url: `http://example.com${fullscreenPath}`,
+ allow_fullscreen: true,
+ children: [
+ {
+ name: "A",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ url: `http://example.org${fullscreenPath}`,
+ allow_fullscreen: true,
+ children: [
+ {
+ name: "C",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ url: `http://example.com${fullscreenPath}`,
+ allow_fullscreen: true,
+ children: [
+ {
+ name: "D",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ url: `http://example.com${fullscreenPath}?different-uri=1`,
+ allow_fullscreen: true,
+ children: [
+ {
+ name: "E",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ url: `http://example.org${fullscreenPath}`,
+ allow_fullscreen: true,
+ children: [],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ name: "B",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ url: `http://example.net${fullscreenPath}`,
+ allow_fullscreen: true,
+ children: [],
+ },
+ ],
+};
+
+add_task(async function test_fullscreen_api_cross_origin_tree() {
+ await new Promise(r => {
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [
+ ["full-screen-api.enabled", true],
+ ["full-screen-api.allow-trusted-requests-only", false],
+ ["full-screen-api.transition-duration.enter", "0 0"],
+ ["full-screen-api.transition-duration.leave", "0 0"],
+ ["dom.security.featurePolicy.header.enabled", true],
+ ["dom.security.featurePolicy.webidl.enabled", true],
+ ],
+ },
+ r
+ );
+ });
+
+ // Register a custom window actor to handle tracking events
+ // and constructing subframes
+ ChromeUtils.registerWindowActor(actorName, {
+ child: {
+ esModuleURI: actorModuleURI,
+ events: {
+ fullscreenchange: { mozSystemGroup: true, capture: true },
+ fullscreenerror: { mozSystemGroup: true, capture: true },
+ },
+ },
+ allFrames: true,
+ });
+
+ let tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ url: frameTree.url,
+ });
+
+ let frames = new Map();
+ async function construct_frame_children(browsingContext, tree) {
+ let actor = browsingContext.currentWindowGlobal.getActor(actorName);
+ frames.set(tree.name, {
+ browsingContext,
+ actor,
+ });
+
+ for (let child of tree.children) {
+ // Create the child IFrame and wait for it to load.
+ let childBC = await actor.sendQuery("CreateChild", child);
+ await construct_frame_children(childBC, child);
+ }
+ }
+
+ await construct_frame_children(tab.linkedBrowser.browsingContext, frameTree);
+
+ async function check_events(expected_events) {
+ for (let [name, expected] of expected_events) {
+ let actor = frames.get(name).actor;
+
+ // Each content process fires the fullscreenchange
+ // event independently and in parallel making it
+ // possible for the promises returned by
+ // `requestFullscreen` or `exitFullscreen` to
+ // resolve before all events have fired. We wait
+ // for the number of events to match before
+ // continuing to ensure we don't miss an expected
+ // event that hasn't fired yet.
+ let events;
+ await TestUtils.waitForCondition(async () => {
+ events = await actor.sendQuery("GetEvents");
+ return events.length == expected.length;
+ }, `Waiting for number of events to match`);
+
+ Assert.equal(events.length, expected.length, "Number of events equal");
+ events.forEach((value, i) => {
+ Assert.equal(value, expected[i], "Event type matches");
+ });
+ }
+ }
+
+ async function check_fullscreenElement(expected_elements) {
+ for (let [name, expected] of expected_elements) {
+ let element = await frames
+ .get(name)
+ .actor.sendQuery("GetFullscreenElement");
+ Assert.equal(element, expected, "The fullScreenElement matches");
+ }
+ }
+
+ // Trigger fullscreen from the target frame.
+ let target = frames.get(fullscreenTarget);
+ await target.actor.sendQuery("RequestFullscreen");
+ // true is fullscreenchange and false is fullscreenerror.
+ await check_events(
+ new Map([
+ ["TOP", [true]],
+ ["A", [true]],
+ ["B", []],
+ ["C", [true]],
+ ["D", [true]],
+ ["E", []],
+ ])
+ );
+ await check_fullscreenElement(
+ new Map([
+ ["TOP", "child_iframe"],
+ ["A", "child_iframe"],
+ ["B", "null"],
+ ["C", "child_iframe"],
+ ["D", "body"],
+ ["E", "null"],
+ ])
+ );
+
+ await target.actor.sendQuery("ExitFullscreen");
+ // fullscreenchange should have fired on exit as well.
+ // true is fullscreenchange and false is fullscreenerror.
+ await check_events(
+ new Map([
+ ["TOP", [true, true]],
+ ["A", [true, true]],
+ ["B", []],
+ ["C", [true, true]],
+ ["D", [true, true]],
+ ["E", []],
+ ])
+ );
+ await check_fullscreenElement(
+ new Map([
+ ["TOP", "null"],
+ ["A", "null"],
+ ["B", "null"],
+ ["C", "null"],
+ ["D", "null"],
+ ["E", "null"],
+ ])
+ );
+
+ // Clear previous events before testing exiting fullscreen with ESC.
+ for (const frame of frames.values()) {
+ frame.actor.sendQuery("ClearEvents");
+ }
+ await target.actor.sendQuery("RequestFullscreen");
+
+ // Escape should cause the proper events to fire and
+ // document.fullscreenElement should be cleared.
+ let finished_exiting = target.actor.sendQuery("WaitForChange");
+ EventUtils.sendKey("ESCAPE");
+ await finished_exiting;
+ // true is fullscreenchange and false is fullscreenerror.
+ await check_events(
+ new Map([
+ ["TOP", [true, true]],
+ ["A", [true, true]],
+ ["B", []],
+ ["C", [true, true]],
+ ["D", [true, true]],
+ ["E", []],
+ ])
+ );
+ await check_fullscreenElement(
+ new Map([
+ ["TOP", "null"],
+ ["A", "null"],
+ ["B", "null"],
+ ["C", "null"],
+ ["D", "null"],
+ ["E", "null"],
+ ])
+ );
+
+ // Remove the tests custom window actor.
+ ChromeUtils.unregisterWindowActor("FullscreenFrame");
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/base/content/test/fullscreen/browser_fullscreen_context_menu.js b/browser/base/content/test/fullscreen/browser_fullscreen_context_menu.js
new file mode 100644
index 0000000000..ec874f1a3f
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_fullscreen_context_menu.js
@@ -0,0 +1,142 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function openContextMenu(itemElement, win = window) {
+ let popupShownPromise = BrowserTestUtils.waitForEvent(
+ itemElement.ownerDocument,
+ "popupshown"
+ );
+ EventUtils.synthesizeMouseAtCenter(
+ itemElement,
+ {
+ type: "contextmenu",
+ button: 2,
+ },
+ win
+ );
+ let { target } = await popupShownPromise;
+ return target;
+}
+
+async function testContextMenu() {
+ await BrowserTestUtils.withNewTab("about:blank", async () => {
+ let panelUIMenuButton = document.getElementById("PanelUI-menu-button");
+ let contextMenu = await openContextMenu(panelUIMenuButton);
+ let array1 = AppConstants.MENUBAR_CAN_AUTOHIDE
+ ? [
+ ".customize-context-moveToPanel",
+ ".customize-context-removeFromToolbar",
+ "#toolbarItemsMenuSeparator",
+ "#toggle_toolbar-menubar",
+ "#toggle_PersonalToolbar",
+ "#viewToolbarsMenuSeparator",
+ ".viewCustomizeToolbar",
+ ]
+ : [
+ ".customize-context-moveToPanel",
+ ".customize-context-removeFromToolbar",
+ "#toolbarItemsMenuSeparator",
+ "#toggle_PersonalToolbar",
+ "#viewToolbarsMenuSeparator",
+ ".viewCustomizeToolbar",
+ ];
+ let result1 = verifyContextMenu(contextMenu, array1);
+ ok(!result1, "Expected no errors verifying context menu items");
+ contextMenu.hidePopup();
+ let onFullscreen = Promise.all([
+ BrowserTestUtils.waitForEvent(window, "fullscreen"),
+ BrowserTestUtils.waitForEvent(
+ window,
+ "sizemodechange",
+ false,
+ e => window.fullScreen
+ ),
+ BrowserTestUtils.waitForPopupEvent(contextMenu, "hidden"),
+ ]);
+ document.getElementById("View:FullScreen").doCommand();
+ contextMenu.hidePopup();
+ info("waiting for fullscreen");
+ await onFullscreen;
+ // make sure the toolbox is visible if it's autohidden
+ document.getElementById("Browser:OpenLocation").doCommand();
+ info("trigger the context menu");
+ let contextMenu2 = await openContextMenu(panelUIMenuButton);
+ info("context menu should be open, verify its menu items");
+ let array2 = AppConstants.MENUBAR_CAN_AUTOHIDE
+ ? [
+ ".customize-context-moveToPanel",
+ ".customize-context-removeFromToolbar",
+ "#toolbarItemsMenuSeparator",
+ "#toggle_toolbar-menubar",
+ "#toggle_PersonalToolbar",
+ "#viewToolbarsMenuSeparator",
+ ".viewCustomizeToolbar",
+ `menuseparator[contexttype="fullscreen"]`,
+ `.fullscreen-context-autohide`,
+ `menuitem[contexttype="fullscreen"]`,
+ ]
+ : [
+ ".customize-context-moveToPanel",
+ ".customize-context-removeFromToolbar",
+ "#toolbarItemsMenuSeparator",
+ "#toggle_PersonalToolbar",
+ "#viewToolbarsMenuSeparator",
+ ".viewCustomizeToolbar",
+ `menuseparator[contexttype="fullscreen"]`,
+ `.fullscreen-context-autohide`,
+ `menuitem[contexttype="fullscreen"]`,
+ ];
+ let result2 = verifyContextMenu(contextMenu2, array2);
+ ok(!result2, "Expected no errors verifying context menu items");
+ let onExitFullscreen = Promise.all([
+ BrowserTestUtils.waitForEvent(window, "fullscreen"),
+ BrowserTestUtils.waitForEvent(
+ window,
+ "sizemodechange",
+ false,
+ e => !window.fullScreen
+ ),
+ BrowserTestUtils.waitForPopupEvent(contextMenu2, "hidden"),
+ ]);
+ document.getElementById("View:FullScreen").doCommand();
+ contextMenu2.hidePopup();
+ await onExitFullscreen;
+ });
+}
+
+function verifyContextMenu(contextMenu, itemSelectors) {
+ // Ignore hidden nodes
+ let items = Array.from(contextMenu.children).filter(n =>
+ BrowserTestUtils.is_visible(n)
+ );
+ let menuAsText = items
+ .map(n => {
+ return n.nodeName == "menuseparator"
+ ? "---"
+ : `${n.label} (${n.command})`;
+ })
+ .join("\n");
+ info("Got actual context menu items: \n" + menuAsText);
+
+ try {
+ is(
+ items.length,
+ itemSelectors.length,
+ "Context menu has the expected number of items"
+ );
+ for (let i = 0; i < items.length; i++) {
+ let selector = itemSelectors[i];
+ ok(
+ items[i].matches(selector),
+ `Item at ${i} matches expected selector: ${selector}`
+ );
+ }
+ } catch (ex) {
+ return ex;
+ }
+ return null;
+}
+
+add_task(testContextMenu);
diff --git a/browser/base/content/test/fullscreen/browser_fullscreen_cross_origin.js b/browser/base/content/test/fullscreen/browser_fullscreen_cross_origin.js
new file mode 100644
index 0000000000..0babb8b35e
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_fullscreen_cross_origin.js
@@ -0,0 +1,64 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const ORIGIN =
+ "https://example.com/browser/browser/base/content/test/fullscreen/fullscreen_frame.html";
+
+add_task(async function test_fullscreen_cross_origin() {
+ async function requestFullscreen(aAllow, aExpect) {
+ await BrowserTestUtils.withNewTab(ORIGIN, async function (browser) {
+ const iframeId = aExpect == "allowed" ? "frameAllowed" : "frameDenied";
+
+ info("Start fullscreen on iframe " + iframeId);
+ await SpecialPowers.spawn(
+ browser,
+ [{ aExpect, iframeId }],
+ async function (args) {
+ let frame = content.document.getElementById(args.iframeId);
+ frame.focus();
+ await SpecialPowers.spawn(frame, [args.aExpect], async expect => {
+ let frameDoc = content.document;
+ const waitForFullscreen = new Promise(resolve => {
+ const message =
+ expect == "allowed" ? "fullscreenchange" : "fullscreenerror";
+ function handler(evt) {
+ frameDoc.removeEventListener(message, handler);
+ Assert.equal(evt.type, message, `Request should be ${expect}`);
+ frameDoc.exitFullscreen();
+ resolve();
+ }
+ frameDoc.addEventListener(message, handler);
+ });
+ frameDoc.getElementById("request").click();
+ await waitForFullscreen;
+ });
+ }
+ );
+
+ if (aExpect == "allowed") {
+ waitForFullScreenState(browser, false);
+ }
+ });
+ }
+
+ await new Promise(r => {
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [
+ ["full-screen-api.enabled", true],
+ ["full-screen-api.allow-trusted-requests-only", false],
+ ["full-screen-api.transition-duration.enter", "0 0"],
+ ["full-screen-api.transition-duration.leave", "0 0"],
+ ["dom.security.featurePolicy.header.enabled", true],
+ ["dom.security.featurePolicy.webidl.enabled", true],
+ ],
+ },
+ r
+ );
+ });
+
+ await requestFullscreen(undefined, "denied");
+ await requestFullscreen("fullscreen", "allowed");
+});
diff --git a/browser/base/content/test/fullscreen/browser_fullscreen_enterInUrlbar.js b/browser/base/content/test/fullscreen/browser_fullscreen_enterInUrlbar.js
new file mode 100644
index 0000000000..6ece64a6f3
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_fullscreen_enterInUrlbar.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This test makes sure that when the user presses enter in the urlbar in full
+// screen, the toolbars are hidden. This should not be run on macOS because we
+// don't hide the toolbars there.
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.sys.mjs",
+});
+
+add_task(async function test() {
+ await BrowserTestUtils.withNewTab("about:blank", async () => {
+ // Do the View:FullScreen command and wait for the transition.
+ let onFullscreen = BrowserTestUtils.waitForEvent(window, "fullscreen");
+ document.getElementById("View:FullScreen").doCommand();
+ await onFullscreen;
+
+ // Do the Browser:OpenLocation command to show the nav toolbox and focus
+ // the urlbar.
+ let onToolboxShown = TestUtils.topicObserved(
+ "fullscreen-nav-toolbox",
+ (subject, data) => data == "shown"
+ );
+ document.getElementById("Browser:OpenLocation").doCommand();
+ info("Waiting for the nav toolbox to be shown");
+ await onToolboxShown;
+
+ // Enter a URL.
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ value: "http://example.com/",
+ waitForFocus: SimpleTest.waitForFocus,
+ fireInputEvent: true,
+ });
+
+ // Press enter and wait for the nav toolbox to be hidden.
+ let onToolboxHidden = TestUtils.topicObserved(
+ "fullscreen-nav-toolbox",
+ (subject, data) => data == "hidden"
+ );
+ EventUtils.synthesizeKey("KEY_Enter");
+ info("Waiting for the nav toolbox to be hidden");
+ await onToolboxHidden;
+
+ Assert.ok(true, "Nav toolbox hidden");
+
+ info("Waiting for exiting from the fullscreen mode...");
+ onFullscreen = BrowserTestUtils.waitForEvent(window, "fullscreen");
+ document.getElementById("View:FullScreen").doCommand();
+ await onFullscreen;
+ });
+});
diff --git a/browser/base/content/test/fullscreen/browser_fullscreen_from_minimize.js b/browser/base/content/test/fullscreen/browser_fullscreen_from_minimize.js
new file mode 100644
index 0000000000..c4ef8fe642
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_fullscreen_from_minimize.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This test checks whether fullscreen windows can transition to minimized windows,
+// and back again. This is sometimes not directly supported by the OS widgets. For
+// example, in macOS, the minimize button is greyed-out in the title bar of
+// fullscreen windows, making this transition impossible for users to initiate.
+// Still, web APIs do allow arbitrary combinations of window calls, and this test
+// exercises some of those combinations.
+
+const restoreWindowToNormal = async () => {
+ // Get the window to normal state by calling window.restore(). This may take
+ // multiple attempts since a call to restore could bring the window to either
+ // NORMAL or MAXIMIZED state.
+ while (window.windowState != window.STATE_NORMAL) {
+ info(
+ `Calling window.restore(), to try to reach "normal" state ${window.STATE_NORMAL}.`
+ );
+ let promiseSizeModeChange = BrowserTestUtils.waitForEvent(
+ window,
+ "sizemodechange"
+ );
+ window.restore();
+ await promiseSizeModeChange;
+ info(`Window reached state ${window.windowState}.`);
+ }
+};
+
+add_task(async function () {
+ registerCleanupFunction(function () {
+ window.restore();
+ });
+
+ // We reuse these variables to create new promises for each transition.
+ let promiseSizeModeChange;
+ let promiseFullscreen;
+
+ await restoreWindowToNormal();
+ ok(!window.fullScreen, "Window should not be fullscreen at start of test.");
+
+ // Get to fullscreen.
+ info("Requesting fullscreen.");
+ promiseFullscreen = document.documentElement.requestFullscreen();
+ await promiseFullscreen;
+ ok(window.fullScreen, "Window should be fullscreen before being minimized.");
+
+ // Transition between fullscreen and minimize states.
+ info("Requesting minimize on a fullscreen window.");
+ promiseSizeModeChange = BrowserTestUtils.waitForEvent(
+ window,
+ "sizemodechange"
+ );
+ window.minimize();
+ await promiseSizeModeChange;
+ is(
+ window.windowState,
+ window.STATE_MINIMIZED,
+ "Window should be minimized after fullscreen."
+ );
+
+ // Whether or not the previous transition worked, restore the window
+ // and then minimize it.
+ await restoreWindowToNormal();
+
+ info("Requesting minimize on a normal window.");
+ promiseSizeModeChange = BrowserTestUtils.waitForEvent(
+ window,
+ "sizemodechange"
+ );
+ window.minimize();
+ await promiseSizeModeChange;
+ is(
+ window.windowState,
+ window.STATE_MINIMIZED,
+ "Window should be minimized before fullscreen."
+ );
+
+ info("Requesting fullscreen on a minimized window.");
+ promiseFullscreen = document.documentElement.requestFullscreen();
+ await promiseFullscreen;
+ ok(window.fullScreen, "Window should be fullscreen after being minimized.");
+});
diff --git a/browser/base/content/test/fullscreen/browser_fullscreen_keydown_reservation.js b/browser/base/content/test/fullscreen/browser_fullscreen_keydown_reservation.js
new file mode 100644
index 0000000000..2d34ac6c7b
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_fullscreen_keydown_reservation.js
@@ -0,0 +1,112 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// This test verifies that whether shortcut keys of toggling fullscreen modes
+// are reserved.
+add_task(async function test_keydown_event_reservation_toggling_fullscreen() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["full-screen-api.transition-duration.enter", "0 0"],
+ ["full-screen-api.transition-duration.leave", "0 0"],
+ ],
+ });
+
+ let shortcutKeys = [{ key: "KEY_F11", modifiers: {} }];
+ if (navigator.platform.startsWith("Mac")) {
+ shortcutKeys.push({
+ key: "f",
+ modifiers: { metaKey: true, ctrlKey: true },
+ });
+ shortcutKeys.push({
+ key: "F",
+ modifiers: { metaKey: true, shiftKey: true },
+ });
+ }
+ function shortcutDescription(aShortcutKey) {
+ return `${
+ aShortcutKey.metaKey ? "Meta + " : ""
+ }${aShortcutKey.shiftKey ? "Shift + " : ""}${aShortcutKey.ctrlKey ? "Ctrl + " : ""}${aShortcutKey.key.replace("KEY_", "")}`;
+ }
+ for (const shortcutKey of shortcutKeys) {
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.org/browser/browser/base/content/test/fullscreen/fullscreen.html"
+ );
+
+ await SimpleTest.promiseFocus(tab.linkedBrowser);
+
+ const fullScreenEntered = BrowserTestUtils.waitForEvent(
+ window,
+ "fullscreen"
+ );
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ content.wrappedJSObject.keydown = null;
+ content.window.addEventListener("keydown", event => {
+ switch (event.key) {
+ case "Shift":
+ case "Meta":
+ case "Control":
+ break;
+ default:
+ content.wrappedJSObject.keydown = event;
+ }
+ });
+ });
+
+ EventUtils.synthesizeKey(shortcutKey.key, shortcutKey.modifiers);
+
+ info(
+ `Waiting for entering the fullscreen mode with synthesizing ${shortcutDescription(
+ shortcutKey
+ )}...`
+ );
+ await fullScreenEntered;
+
+ info("Retrieving the result...");
+ Assert.ok(
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ async () => !!content.wrappedJSObject.keydown
+ ),
+ `Entering the fullscreen mode with ${shortcutDescription(
+ shortcutKey
+ )} should cause "keydown" event`
+ );
+
+ const fullScreenExited = BrowserTestUtils.waitForEvent(
+ window,
+ "fullscreen"
+ );
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ content.wrappedJSObject.keydown = null;
+ });
+
+ EventUtils.synthesizeKey(shortcutKey.key, shortcutKey.modifiers);
+
+ info(
+ `Waiting for exiting from the fullscreen mode with synthesizing ${shortcutDescription(
+ shortcutKey
+ )}...`
+ );
+ await fullScreenExited;
+
+ info("Retrieving the result...");
+ Assert.ok(
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ async () => !content.wrappedJSObject.keydown
+ ),
+ `Exiting from the fullscreen mode with ${shortcutDescription(
+ shortcutKey
+ )} should not cause "keydown" event`
+ );
+
+ BrowserTestUtils.removeTab(tab);
+ }
+});
diff --git a/browser/base/content/test/fullscreen/browser_fullscreen_menus.js b/browser/base/content/test/fullscreen/browser_fullscreen_menus.js
new file mode 100644
index 0000000000..90dd06192d
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_fullscreen_menus.js
@@ -0,0 +1,72 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_shortcut_key_label_in_fullscreen_menu_item() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["full-screen-api.transition-duration.enter", "0 0"],
+ ["full-screen-api.transition-duration.leave", "0 0"],
+ ],
+ });
+
+ const isMac = AppConstants.platform == "macosx";
+ const shortCutKeyLabel = isMac ? "\u2303\u2318F" : "F11";
+ const enterMenuItemId = isMac ? "enterFullScreenItem" : "fullScreenItem";
+ const exitMenuItemId = isMac ? "exitFullScreenItem" : "fullScreenItem";
+ const accelKeyLabelSelector = ".menu-accel-container > label";
+
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.org/browser/browser/base/content/test/fullscreen/fullscreen.html"
+ );
+
+ await SimpleTest.promiseFocus(tab.linkedBrowser);
+
+ document.getElementById(enterMenuItemId).render();
+ Assert.equal(
+ document
+ .getElementById(enterMenuItemId)
+ .querySelector(accelKeyLabelSelector)
+ ?.getAttribute("value"),
+ shortCutKeyLabel,
+ `The menu item to enter into the fullscreen mode should show a shortcut key`
+ );
+
+ const fullScreenEntered = BrowserTestUtils.waitForEvent(window, "fullscreen");
+
+ EventUtils.synthesizeKey("KEY_F11", {});
+
+ info(`Waiting for entering the fullscreen mode...`);
+ await fullScreenEntered;
+
+ document.getElementById(exitMenuItemId).render();
+ Assert.equal(
+ document
+ .getElementById(exitMenuItemId)
+ .querySelector(accelKeyLabelSelector)
+ ?.getAttribute("value"),
+ shortCutKeyLabel,
+ `The menu item to exiting from the fullscreen mode should show a shortcut key`
+ );
+
+ const fullScreenExited = BrowserTestUtils.waitForEvent(window, "fullscreen");
+
+ EventUtils.synthesizeKey("KEY_F11", {});
+
+ info(`Waiting for exiting from the fullscreen mode...`);
+ await fullScreenExited;
+
+ document.getElementById(enterMenuItemId).render();
+ Assert.equal(
+ document
+ .getElementById(enterMenuItemId)
+ .querySelector(accelKeyLabelSelector)
+ ?.getAttribute("value"),
+ shortCutKeyLabel,
+ `After exiting from the fullscreen mode, the menu item to enter the fullscreen mode should show a shortcut key`
+ );
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/base/content/test/fullscreen/browser_fullscreen_newtab.js b/browser/base/content/test/fullscreen/browser_fullscreen_newtab.js
new file mode 100644
index 0000000000..d5a74a0aa3
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_fullscreen_newtab.js
@@ -0,0 +1,55 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// This test verifies that when in fullscreen mode, and a new tab is opened,
+// fullscreen mode is exited and the url bar is focused.
+add_task(async function test_fullscreen_display_none() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["full-screen-api.enabled", true],
+ ["full-screen-api.allow-trusted-requests-only", false],
+ ],
+ });
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.org/browser/browser/base/content/test/fullscreen/fullscreen.html"
+ );
+
+ let fullScreenEntered = BrowserTestUtils.waitForEvent(
+ document,
+ "fullscreenchange",
+ false,
+ () => document.fullscreenElement
+ );
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ content.document.getElementById("request").click();
+ });
+
+ await fullScreenEntered;
+
+ let fullScreenExited = BrowserTestUtils.waitForEvent(
+ document,
+ "fullscreenchange",
+ false,
+ () => !document.fullscreenElement
+ );
+
+ let focusPromise = BrowserTestUtils.waitForEvent(window, "focus");
+ EventUtils.synthesizeKey("T", { accelKey: true });
+ await focusPromise;
+
+ is(
+ document.activeElement,
+ gURLBar.inputField,
+ "url bar is focused after new tab opened"
+ );
+
+ await fullScreenExited;
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/base/content/test/fullscreen/browser_fullscreen_newwindow.js b/browser/base/content/test/fullscreen/browser_fullscreen_newwindow.js
new file mode 100644
index 0000000000..dee02e2db0
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_fullscreen_newwindow.js
@@ -0,0 +1,83 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// This test verifies that when in fullscreen mode, and a new window is opened,
+// fullscreen mode should not exit and the url bar is focused.
+add_task(async function test_fullscreen_new_window() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["full-screen-api.enabled", true],
+ ["full-screen-api.allow-trusted-requests-only", false],
+ ],
+ });
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.org/browser/browser/base/content/test/fullscreen/fullscreen.html"
+ );
+
+ let fullScreenEntered = BrowserTestUtils.waitForEvent(
+ document,
+ "fullscreenchange",
+ false,
+ () => document.fullscreenElement
+ );
+
+ // Enter fullscreen.
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ content.document.getElementById("request").click();
+ });
+
+ await fullScreenEntered;
+
+ // Open a new window via ctrl+n.
+ let newWindowPromise = BrowserTestUtils.waitForNewWindow({
+ url: "about:blank",
+ });
+ EventUtils.synthesizeKey("N", { accelKey: true });
+ let newWindow = await newWindowPromise;
+
+ // Check new window state.
+ is(
+ newWindow.document.activeElement,
+ newWindow.gURLBar.inputField,
+ "url bar is focused after new window opened"
+ );
+ ok(
+ !newWindow.fullScreen,
+ "The new chrome window should not be in fullscreen"
+ );
+ ok(
+ !newWindow.document.documentElement.hasAttribute("inDOMFullscreen"),
+ "The new chrome document should not be in fullscreen"
+ );
+
+ // Wait a bit then check the original window state.
+ await new Promise(resolve => TestUtils.executeSoon(resolve));
+ ok(
+ window.fullScreen,
+ "The original chrome window should be still in fullscreen"
+ );
+ ok(
+ document.documentElement.hasAttribute("inDOMFullscreen"),
+ "The original chrome document should be still in fullscreen"
+ );
+
+ // Close new window and move focus back to original window.
+ await BrowserTestUtils.closeWindow(newWindow);
+ await SimpleTest.promiseFocus(window);
+
+ // Exit fullscreen on original window.
+ let fullScreenExited = BrowserTestUtils.waitForEvent(
+ document,
+ "fullscreenchange",
+ false,
+ () => !document.fullscreenElement
+ );
+ EventUtils.synthesizeKey("KEY_Escape");
+ await fullScreenExited;
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/base/content/test/fullscreen/browser_fullscreen_permissions_prompt.js b/browser/base/content/test/fullscreen/browser_fullscreen_permissions_prompt.js
new file mode 100644
index 0000000000..82f0c97631
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_fullscreen_permissions_prompt.js
@@ -0,0 +1,160 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// This test tends to trigger a race in the fullscreen time telemetry,
+// where the fullscreen enter and fullscreen exit events (which use the
+// same histogram ID) overlap. That causes TelemetryStopwatch to log an
+// error.
+SimpleTest.ignoreAllUncaughtExceptions(true);
+const { PromiseTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PromiseTestUtils.sys.mjs"
+);
+PromiseTestUtils.allowMatchingRejectionsGlobally(/Not in fullscreen mode/);
+
+SimpleTest.requestCompleteLog();
+
+async function requestNotificationPermission(browser) {
+ return SpecialPowers.spawn(browser, [], () => {
+ return content.Notification.requestPermission();
+ });
+}
+
+async function requestCameraPermission(browser) {
+ return SpecialPowers.spawn(browser, [], () =>
+ content.navigator.mediaDevices
+ .getUserMedia({ video: true, fake: true })
+ .then(
+ () => true,
+ () => false
+ )
+ );
+}
+
+add_task(async function test_fullscreen_closes_permissionui_prompt() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.webnotifications.requireuserinteraction", false],
+ ["permissions.fullscreen.allowed", false],
+ ],
+ });
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.com"
+ );
+ let browser = tab.linkedBrowser;
+
+ let popupShown, requestResult, popupHidden;
+
+ popupShown = BrowserTestUtils.waitForEvent(
+ window.PopupNotifications.panel,
+ "popupshown"
+ );
+
+ info("Requesting notification permission");
+ requestResult = requestNotificationPermission(browser);
+ await popupShown;
+
+ info("Entering DOM full-screen");
+ popupHidden = BrowserTestUtils.waitForEvent(
+ window.PopupNotifications.panel,
+ "popuphidden"
+ );
+
+ await changeFullscreen(browser, true);
+
+ await popupHidden;
+
+ is(
+ await requestResult,
+ "default",
+ "Expect permission request to be cancelled"
+ );
+
+ await changeFullscreen(browser, false);
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async function test_fullscreen_closes_webrtc_permission_prompt() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["media.navigator.permission.fake", true],
+ ["media.navigator.permission.force", true],
+ ["permissions.fullscreen.allowed", false],
+ ],
+ });
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.com"
+ );
+ let browser = tab.linkedBrowser;
+ let popupShown, requestResult, popupHidden;
+
+ popupShown = BrowserTestUtils.waitForEvent(
+ window.PopupNotifications.panel,
+ "popupshown"
+ );
+
+ info("Requesting camera permission");
+ requestResult = requestCameraPermission(browser);
+
+ await popupShown;
+
+ info("Entering DOM full-screen");
+ popupHidden = BrowserTestUtils.waitForEvent(
+ window.PopupNotifications.panel,
+ "popuphidden"
+ );
+ await changeFullscreen(browser, true);
+
+ await popupHidden;
+
+ is(
+ await requestResult,
+ false,
+ "Expect webrtc permission request to be cancelled"
+ );
+
+ await changeFullscreen(browser, false);
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async function test_permission_prompt_closes_fullscreen() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.webnotifications.requireuserinteraction", false],
+ ["permissions.fullscreen.allowed", false],
+ ],
+ });
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.com"
+ );
+ let browser = tab.linkedBrowser;
+ info("Entering DOM full-screen");
+ await changeFullscreen(browser, true);
+
+ let popupShown = BrowserTestUtils.waitForEvent(
+ window.PopupNotifications.panel,
+ "popupshown"
+ );
+ let fullScreenExit = waitForFullScreenState(browser, false);
+
+ info("Requesting notification permission");
+ requestNotificationPermission(browser).catch(() => {});
+ await popupShown;
+
+ info("Waiting for full-screen exit");
+ await fullScreenExit;
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
diff --git a/browser/base/content/test/fullscreen/browser_fullscreen_warning.js b/browser/base/content/test/fullscreen/browser_fullscreen_warning.js
new file mode 100644
index 0000000000..b8bab5f90c
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_fullscreen_warning.js
@@ -0,0 +1,280 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function checkWarningState(aWarningElement, aExpectedState, aMsg) {
+ ["hidden", "ontop", "onscreen"].forEach(state => {
+ is(
+ aWarningElement.hasAttribute(state),
+ state == aExpectedState,
+ `${aMsg} - check ${state} attribute.`
+ );
+ });
+}
+
+async function waitForWarningState(aWarningElement, aExpectedState) {
+ await BrowserTestUtils.waitForAttribute(aExpectedState, aWarningElement, "");
+ checkWarningState(
+ aWarningElement,
+ aExpectedState,
+ `Wait for ${aExpectedState} state`
+ );
+}
+
+add_setup(async function init() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["full-screen-api.enabled", true],
+ ["full-screen-api.allow-trusted-requests-only", false],
+ ],
+ });
+});
+
+add_task(async function test_fullscreen_display_none() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: `data:text/html,
+ <html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Fullscreen Test</title>
+ </head>
+ <body id="body">
+ <iframe
+ src="https://example.org/browser/browser/base/content/test/fullscreen/fullscreen.html"
+ hidden
+ allowfullscreen></iframe>
+ </body>
+ </html>`,
+ },
+ async function (browser) {
+ let warning = document.getElementById("fullscreen-warning");
+ checkWarningState(
+ warning,
+ "hidden",
+ "Should not show full screen warning initially"
+ );
+
+ let warningShownPromise = waitForWarningState(warning, "onscreen");
+ // Enter fullscreen
+ await SpecialPowers.spawn(browser, [], async () => {
+ let frame = content.document.querySelector("iframe");
+ frame.focus();
+ await SpecialPowers.spawn(frame, [], () => {
+ content.document.getElementById("request").click();
+ });
+ });
+ await warningShownPromise;
+ ok(true, "Fullscreen warning shown");
+ // Exit fullscreen
+ let exitFullscreenPromise = BrowserTestUtils.waitForEvent(
+ document,
+ "fullscreenchange",
+ false,
+ () => !document.fullscreenElement
+ );
+ document.getElementById("fullscreen-exit-button").click();
+ await exitFullscreenPromise;
+
+ checkWarningState(
+ warning,
+ "hidden",
+ "Should hide fullscreen warning after exiting fullscreen"
+ );
+ }
+ );
+});
+
+add_task(async function test_fullscreen_pointerlock_conflict() {
+ await BrowserTestUtils.withNewTab("https://example.com", async browser => {
+ let fsWarning = document.getElementById("fullscreen-warning");
+ let plWarning = document.getElementById("pointerlock-warning");
+
+ checkWarningState(
+ fsWarning,
+ "hidden",
+ "Should not show full screen warning initially"
+ );
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning initially"
+ );
+
+ let fsWarningShownPromise = waitForWarningState(fsWarning, "onscreen");
+ info("Entering full screen and pointer lock.");
+ await SpecialPowers.spawn(browser, [], async () => {
+ await content.document.body.requestFullscreen();
+ await content.document.body.requestPointerLock();
+ });
+
+ await fsWarningShownPromise;
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning"
+ );
+
+ info("Exiting pointerlock");
+ await SpecialPowers.spawn(browser, [], async () => {
+ await content.document.exitPointerLock();
+ });
+
+ checkWarningState(
+ fsWarning,
+ "onscreen",
+ "Should still show full screen warning"
+ );
+ checkWarningState(
+ plWarning,
+ "hidden",
+ "Should not show pointer lock warning"
+ );
+
+ // Cleanup
+ info("Exiting fullscreen");
+ await document.exitFullscreen();
+ });
+});
+
+// https://bugzilla.mozilla.org/show_bug.cgi?id=1821884
+add_task(async function test_reshow_fullscreen_notification() {
+ await BrowserTestUtils.withNewTab("https://example.com", async browser => {
+ let newWin = await BrowserTestUtils.openNewBrowserWindow();
+ let fsWarning = document.getElementById("fullscreen-warning");
+
+ info("Entering full screen and wait for the fullscreen warning to appear.");
+ await SimpleTest.promiseFocus(window);
+ await Promise.all([
+ waitForWarningState(fsWarning, "onscreen"),
+ BrowserTestUtils.waitForEvent(fsWarning, "transitionend"),
+ SpecialPowers.spawn(browser, [], async () => {
+ content.document.body.requestFullscreen();
+ }),
+ ]);
+
+ info(
+ "Switch focus away from the fullscreen window, the fullscreen warning should still hide automatically."
+ );
+ await Promise.all([
+ waitForWarningState(fsWarning, "hidden"),
+ SimpleTest.promiseFocus(newWin),
+ ]);
+
+ info(
+ "Switch focus back to the fullscreen window, the fullscreen warning should show again."
+ );
+ await Promise.all([
+ waitForWarningState(fsWarning, "onscreen"),
+ SimpleTest.promiseFocus(window),
+ ]);
+
+ info("Wait for fullscreen warning timed out.");
+ await waitForWarningState(fsWarning, "hidden");
+
+ info("The fullscreen warning should not show again.");
+ await SimpleTest.promiseFocus(newWin);
+ await SimpleTest.promiseFocus(window);
+ await new Promise(resolve => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(resolve);
+ });
+ });
+ checkWarningState(
+ fsWarning,
+ "hidden",
+ "The fullscreen warning should not show."
+ );
+
+ info("Close new browser window.");
+ await BrowserTestUtils.closeWindow(newWin);
+
+ info("Exit fullscreen.");
+ await document.exitFullscreen();
+ });
+});
+
+add_task(async function test_fullscreen_reappear() {
+ await BrowserTestUtils.withNewTab("https://example.com", async browser => {
+ let fsWarning = document.getElementById("fullscreen-warning");
+
+ info("Entering full screen and wait for the fullscreen warning to appear.");
+ await Promise.all([
+ waitForWarningState(fsWarning, "onscreen"),
+ SpecialPowers.spawn(browser, [], async () => {
+ content.document.body.requestFullscreen();
+ }),
+ ]);
+
+ info("Wait for fullscreen warning timed out.");
+ await waitForWarningState(fsWarning, "hidden");
+
+ info("Move mouse to the top of screen.");
+ await Promise.all([
+ waitForWarningState(fsWarning, "ontop"),
+ EventUtils.synthesizeMouse(document.documentElement, 100, 0, {
+ type: "mousemove",
+ }),
+ ]);
+
+ info("Wait for fullscreen warning timed out again.");
+ await waitForWarningState(fsWarning, "hidden");
+
+ info("Exit fullscreen.");
+ await document.exitFullscreen();
+ });
+});
+
+// https://bugzilla.mozilla.org/show_bug.cgi?id=1847901
+add_task(async function test_fullscreen_warning_disabled() {
+ // Disable fullscreen warning
+ await SpecialPowers.pushPrefEnv({
+ set: [["full-screen-api.warning.timeout", 0]],
+ });
+
+ await BrowserTestUtils.withNewTab("https://example.com", async browser => {
+ let newWin = await BrowserTestUtils.openNewBrowserWindow();
+ let fsWarning = document.getElementById("fullscreen-warning");
+ let mut = new MutationObserver(mutations => {
+ ok(false, `${mutations[0].attributeName} attribute should not change`);
+ });
+ mut.observe(fsWarning, {
+ attributeFilter: ["hidden", "onscreen", "ontop"],
+ });
+
+ info("Entering full screen.");
+ await SimpleTest.promiseFocus(window);
+ await SpecialPowers.spawn(browser, [], async () => {
+ return content.document.body.requestFullscreen();
+ });
+ // Wait a bit to ensure no state change.
+ await new Promise(resolve => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(resolve);
+ });
+ });
+
+ info("The fullscreen warning should still not show after switching focus.");
+ await SimpleTest.promiseFocus(newWin);
+ await SimpleTest.promiseFocus(window);
+ // Wait a bit to ensure no state change.
+ await new Promise(resolve => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(resolve);
+ });
+ });
+
+ mut.disconnect();
+
+ info("Close new browser window.");
+ await BrowserTestUtils.closeWindow(newWin);
+
+ info("Exit fullscreen.");
+ await document.exitFullscreen();
+ });
+
+ // Revert the setting to avoid affecting subsequent tests.
+ await SpecialPowers.popPrefEnv();
+});
diff --git a/browser/base/content/test/fullscreen/browser_fullscreen_window_focus.js b/browser/base/content/test/fullscreen/browser_fullscreen_window_focus.js
new file mode 100644
index 0000000000..7c935f64d3
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_fullscreen_window_focus.js
@@ -0,0 +1,110 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function pause() {
+ /* eslint-disable mozilla/no-arbitrary-setTimeout */
+ return new Promise(resolve => setTimeout(resolve, 500));
+}
+
+// This test tends to trigger a race in the fullscreen time telemetry,
+// where the fullscreen enter and fullscreen exit events (which use the
+// same histogram ID) overlap. That causes TelemetryStopwatch to log an
+// error.
+SimpleTest.ignoreAllUncaughtExceptions(true);
+
+const IFRAME_ID = "testIframe";
+
+async function testWindowFocus(isPopup, iframeID) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+ info("Calling window.open()");
+ let openedWindow = await jsWindowOpen(tab.linkedBrowser, isPopup, iframeID);
+ info("Letting OOP focus to stabilize");
+ await pause(); // Bug 1719659 for proper fix
+ info("re-focusing main window");
+ await waitForFocus(tab.linkedBrowser);
+
+ info("Entering full-screen");
+ await changeFullscreen(tab.linkedBrowser, true);
+
+ await testExpectFullScreenExit(tab.linkedBrowser, true, async () => {
+ info("Calling window.focus()");
+ await jsWindowFocus(tab.linkedBrowser, iframeID);
+ });
+
+ // Cleanup
+ if (isPopup) {
+ openedWindow.close();
+ } else {
+ BrowserTestUtils.removeTab(openedWindow);
+ }
+ BrowserTestUtils.removeTab(tab);
+}
+
+async function testWindowElementFocus(isPopup) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+ info("Calling window.open()");
+ let openedWindow = await jsWindowOpen(tab.linkedBrowser, isPopup);
+ info("Letting OOP focus to stabilize");
+ await pause(); // Bug 1719659 for proper fix
+ info("re-focusing main window");
+ await waitForFocus(tab.linkedBrowser);
+
+ info("Entering full-screen");
+ await changeFullscreen(tab.linkedBrowser, true);
+
+ await testExpectFullScreenExit(tab.linkedBrowser, false, async () => {
+ info("Calling element.focus() on popup");
+ await ContentTask.spawn(tab.linkedBrowser, {}, async args => {
+ await content.wrappedJSObject.sendMessage(
+ content.wrappedJSObject.openedWindow,
+ "elementfocus"
+ );
+ });
+ });
+
+ // Cleanup
+ await changeFullscreen(tab.linkedBrowser, false);
+ if (isPopup) {
+ openedWindow.close();
+ } else {
+ BrowserTestUtils.removeTab(openedWindow);
+ }
+ BrowserTestUtils.removeTab(tab);
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.disable_open_during_load", false], // Allow window.focus calls without user interaction
+ ["browser.link.open_newwindow.disabled_in_fullscreen", false],
+ ],
+ });
+});
+
+add_task(function test_popupWindowFocus() {
+ return testWindowFocus(true);
+});
+
+add_task(function test_iframePopupWindowFocus() {
+ return testWindowFocus(true, IFRAME_ID);
+});
+
+add_task(function test_popupWindowElementFocus() {
+ return testWindowElementFocus(true);
+});
+
+add_task(function test_backgroundTabFocus() {
+ return testWindowFocus(false);
+});
+
+add_task(function test_iframebackgroundTabFocus() {
+ return testWindowFocus(false, IFRAME_ID);
+});
+
+add_task(function test_backgroundTabElementFocus() {
+ return testWindowElementFocus(false);
+});
diff --git a/browser/base/content/test/fullscreen/browser_fullscreen_window_open.js b/browser/base/content/test/fullscreen/browser_fullscreen_window_open.js
new file mode 100644
index 0000000000..aafed57c75
--- /dev/null
+++ b/browser/base/content/test/fullscreen/browser_fullscreen_window_open.js
@@ -0,0 +1,102 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// This test tends to trigger a race in the fullscreen time telemetry,
+// where the fullscreen enter and fullscreen exit events (which use the
+// same histogram ID) overlap. That causes TelemetryStopwatch to log an
+// error.
+SimpleTest.ignoreAllUncaughtExceptions(true);
+SimpleTest.requestLongerTimeout(2);
+
+const IFRAME_ID = "testIframe";
+
+async function testWindowOpen(iframeID) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+ info("Entering full-screen");
+ await changeFullscreen(tab.linkedBrowser, true);
+
+ let popup;
+ await testExpectFullScreenExit(tab.linkedBrowser, true, async () => {
+ info("Calling window.open()");
+ popup = await jsWindowOpen(tab.linkedBrowser, true, iframeID);
+ });
+
+ // Cleanup
+ await BrowserTestUtils.closeWindow(popup);
+ BrowserTestUtils.removeTab(tab);
+}
+
+async function testWindowOpenExistingWindow(funToOpenExitingWindow, iframeID) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+ let popup = await jsWindowOpen(tab.linkedBrowser, true);
+
+ info("re-focusing main window");
+ await waitForFocus(tab.linkedBrowser);
+
+ info("Entering full-screen");
+ await changeFullscreen(tab.linkedBrowser, true);
+
+ info("open existing popup window");
+ await testExpectFullScreenExit(tab.linkedBrowser, true, async () => {
+ await funToOpenExitingWindow(tab.linkedBrowser, iframeID);
+ });
+
+ // Cleanup
+ await BrowserTestUtils.closeWindow(popup);
+ BrowserTestUtils.removeTab(tab);
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.disable_open_during_load", false], // Allow window.open calls without user interaction
+ ["browser.link.open_newwindow.disabled_in_fullscreen", false],
+ ],
+ });
+});
+
+add_task(function test_parentWindowOpen() {
+ return testWindowOpen();
+});
+
+add_task(function test_iframeWindowOpen() {
+ return testWindowOpen(IFRAME_ID);
+});
+
+add_task(async function test_parentWindowOpenExistWindow() {
+ await testWindowOpenExistingWindow(browser => {
+ info(
+ "Calling window.open() with same name again should reuse the existing window"
+ );
+ jsWindowOpen(browser, true);
+ });
+});
+
+add_task(async function test_iframeWindowOpenExistWindow() {
+ await testWindowOpenExistingWindow((browser, iframeID) => {
+ info(
+ "Calling window.open() with same name again should reuse the existing window"
+ );
+ jsWindowOpen(browser, true, iframeID);
+ }, IFRAME_ID);
+});
+
+add_task(async function test_parentWindowClickLinkOpenExistWindow() {
+ await testWindowOpenExistingWindow(browser => {
+ info(
+ "Clicking link with same target name should reuse the existing window"
+ );
+ jsClickLink(browser, true);
+ });
+});
+
+add_task(async function test_iframeWindowClickLinkOpenExistWindow() {
+ await testWindowOpenExistingWindow((browser, iframeID) => {
+ info(
+ "Clicking link with same target name should reuse the existing window"
+ );
+ jsClickLink(browser, true, iframeID);
+ }, IFRAME_ID);
+});
diff --git a/browser/base/content/test/fullscreen/fullscreen.html b/browser/base/content/test/fullscreen/fullscreen.html
new file mode 100644
index 0000000000..8b4289bb36
--- /dev/null
+++ b/browser/base/content/test/fullscreen/fullscreen.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<script>
+function requestFScreen() {
+ document.body.requestFullscreen();
+}
+</script>
+<body>
+<button id="request" onclick="requestFScreen()"> Fullscreen </button>
+<button id="focus"> Fullscreen </button>
+</body>
+</html>
diff --git a/browser/base/content/test/fullscreen/fullscreen_frame.html b/browser/base/content/test/fullscreen/fullscreen_frame.html
new file mode 100644
index 0000000000..ca1b1a4dd8
--- /dev/null
+++ b/browser/base/content/test/fullscreen/fullscreen_frame.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+ <iframe id="frameAllowed"
+ src="https://example.org/browser/browser/base/content/test/fullscreen/fullscreen.html"
+ allowfullscreen></iframe>
+ <iframe id="frameDenied" src="https://example.org/browser/browser/base/content/test/fullscreen/fullscreen.html"></iframe>
+</body>
+</html>
diff --git a/browser/base/content/test/fullscreen/head.js b/browser/base/content/test/fullscreen/head.js
new file mode 100644
index 0000000000..ce7a4b23b2
--- /dev/null
+++ b/browser/base/content/test/fullscreen/head.js
@@ -0,0 +1,164 @@
+const TEST_URL =
+ "https://example.com/browser/browser/base/content/test/fullscreen/open_and_focus_helper.html";
+
+function waitForFullScreenState(browser, state) {
+ return new Promise(resolve => {
+ let eventReceived = false;
+
+ let observe = (subject, topic, data) => {
+ if (!eventReceived) {
+ return;
+ }
+ Services.obs.removeObserver(observe, "fullscreen-painted");
+ resolve();
+ };
+ Services.obs.addObserver(observe, "fullscreen-painted");
+
+ browser.ownerGlobal.addEventListener(
+ `MozDOMFullscreen:${state ? "Entered" : "Exited"}`,
+ () => {
+ eventReceived = true;
+ },
+ { once: true }
+ );
+ });
+}
+
+/**
+ * Spawns content task in browser to enter / leave fullscreen
+ * @param browser - Browser to use for JS fullscreen requests
+ * @param {Boolean} fullscreenState - true to enter fullscreen, false to leave
+ * @returns {Promise} - Resolves once fullscreen change is applied
+ */
+async function changeFullscreen(browser, fullScreenState) {
+ await new Promise(resolve =>
+ SimpleTest.waitForFocus(resolve, browser.ownerGlobal)
+ );
+ let fullScreenChange = waitForFullScreenState(browser, fullScreenState);
+ SpecialPowers.spawn(browser, [fullScreenState], async state => {
+ // Wait for document focus before requesting full-screen
+ await ContentTaskUtils.waitForCondition(
+ () => content.browsingContext.isActive && content.document.hasFocus(),
+ "Waiting for document focus"
+ );
+ if (state) {
+ content.document.body.requestFullscreen();
+ } else {
+ content.document.exitFullscreen();
+ }
+ });
+ return fullScreenChange;
+}
+
+async function testExpectFullScreenExit(browser, leaveFS, action) {
+ let fsPromise = waitForFullScreenState(browser, false);
+ if (leaveFS) {
+ if (action) {
+ await action();
+ }
+ await fsPromise;
+ ok(true, "Should leave full-screen");
+ } else {
+ if (action) {
+ await action();
+ }
+ let result = await Promise.race([
+ fsPromise,
+ new Promise(resolve => {
+ SimpleTest.requestFlakyTimeout("Wait for failure condition");
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ setTimeout(() => resolve(true), 2500);
+ }),
+ ]);
+ ok(result, "Should not leave full-screen");
+ }
+}
+
+function jsWindowFocus(browser, iframeId) {
+ return ContentTask.spawn(browser, { iframeId }, async args => {
+ let destWin = content;
+ if (args.iframeId) {
+ let iframe = content.document.getElementById(args.iframeId);
+ if (!iframe) {
+ throw new Error("iframe not set");
+ }
+ destWin = iframe.contentWindow;
+ }
+ await content.wrappedJSObject.sendMessage(destWin, "focus");
+ });
+}
+
+function jsElementFocus(browser, iframeId) {
+ return ContentTask.spawn(browser, { iframeId }, async args => {
+ let destWin = content;
+ if (args.iframeId) {
+ let iframe = content.document.getElementById(args.iframeId);
+ if (!iframe) {
+ throw new Error("iframe not set");
+ }
+ destWin = iframe.contentWindow;
+ }
+ await content.wrappedJSObject.sendMessage(destWin, "elementfocus");
+ });
+}
+
+async function jsWindowOpen(browser, isPopup, iframeId) {
+ //let windowOpened = BrowserTestUtils.waitForNewWindow();
+ let windowOpened = isPopup
+ ? BrowserTestUtils.waitForNewWindow({ url: TEST_URL })
+ : BrowserTestUtils.waitForNewTab(gBrowser, TEST_URL, true);
+ ContentTask.spawn(browser, { isPopup, iframeId }, async args => {
+ let destWin = content;
+ if (args.iframeId) {
+ // Create a cross origin iframe
+ destWin = (
+ await content.wrappedJSObject.createIframe(args.iframeId, true)
+ ).contentWindow;
+ }
+ // Send message to either the iframe or the current page to open a popup
+ await content.wrappedJSObject.sendMessage(
+ destWin,
+ args.isPopup ? "openpopup" : "open"
+ );
+ });
+ return windowOpened;
+}
+
+async function jsClickLink(browser, isPopup, iframeId) {
+ //let windowOpened = BrowserTestUtils.waitForNewWindow();
+ let windowOpened = isPopup
+ ? BrowserTestUtils.waitForNewWindow({ url: TEST_URL })
+ : BrowserTestUtils.waitForNewTab(gBrowser, TEST_URL, true);
+ ContentTask.spawn(browser, { isPopup, iframeId }, async args => {
+ let destWin = content;
+ if (args.iframeId) {
+ // Create a cross origin iframe
+ destWin = (
+ await content.wrappedJSObject.createIframe(args.iframeId, true)
+ ).contentWindow;
+ }
+ // Send message to either the iframe or the current page to click a link
+ await content.wrappedJSObject.sendMessage(destWin, "clicklink");
+ });
+ return windowOpened;
+}
+
+function waitForFocus(...args) {
+ return new Promise(resolve => SimpleTest.waitForFocus(resolve, ...args));
+}
+
+function waitForBrowserWindowActive(win) {
+ return new Promise(resolve => {
+ if (Services.focus.activeWindow == win) {
+ resolve();
+ } else {
+ win.addEventListener(
+ "activate",
+ () => {
+ resolve();
+ },
+ { once: true }
+ );
+ }
+ });
+}
diff --git a/browser/base/content/test/fullscreen/open_and_focus_helper.html b/browser/base/content/test/fullscreen/open_and_focus_helper.html
new file mode 100644
index 0000000000..06d1800714
--- /dev/null
+++ b/browser/base/content/test/fullscreen/open_and_focus_helper.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset='utf-8'>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+ <input></input><br>
+ <a href="https://example.com" target="test">link</a>
+ <script>
+ const MY_ORIGIN = window.location.origin;
+ const CROSS_ORIGIN = "https://example.org";
+
+ // Creates an iframe with message channel to trigger window open and focus
+ window.createIframe = function(id, crossOrigin = false) {
+ return new Promise(resolve => {
+ const origin = crossOrigin ? CROSS_ORIGIN : MY_ORIGIN;
+ let iframe = document.createElement("iframe");
+ iframe.id = id;
+ iframe.src = origin + window.location.pathname;
+ iframe.onload = () => resolve(iframe);
+ document.body.appendChild(iframe);
+ });
+ }
+
+ window.sendMessage = function(destWin, msg) {
+ return new Promise(resolve => {
+ let channel = new MessageChannel();
+ channel.port1.onmessage = resolve;
+ destWin.postMessage(msg, "*", [channel.port2]);
+ });
+ }
+
+ window.onMessage = function(event) {
+ let canReply = event.ports && !!event.ports.length;
+ if(event.data === "open") {
+ window.openedWindow = window.open('https://example.com' + window.location.pathname);
+ if (canReply) event.ports[0].postMessage('opened');
+ } else if(event.data === "openpopup") {
+ window.openedWindow = window.open('https://example.com' + window.location.pathname, 'test', 'top=0,height=1, width=300');
+ if (canReply) event.ports[0].postMessage('popupopened');
+ } else if(event.data === "focus") {
+ window.openedWindow.focus();
+ if (canReply) event.ports[0].postMessage('focused');
+ } else if(event.data === "elementfocus") {
+ document.querySelector("input").focus();
+ if (canReply) event.ports[0].postMessage('elementfocused');
+ } else if(event.data === "clicklink") {
+ synthesizeMouseAtCenter(document.querySelector("a"), {});
+ if (canReply) event.ports[0].postMessage('linkclicked');
+ }
+ }
+ window.addEventListener('message', window.onMessage);
+ </script>
+</body>
+</html>