diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /browser/base/content/test/fullscreen | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/base/content/test/fullscreen')
17 files changed, 1529 insertions, 0 deletions
diff --git a/browser/base/content/test/fullscreen/FullscreenFrame.jsm b/browser/base/content/test/fullscreen/FullscreenFrame.jsm new file mode 100644 index 0000000000..28fabcd7f2 --- /dev/null +++ b/browser/base/content/test/fullscreen/FullscreenFrame.jsm @@ -0,0 +1,105 @@ +/* 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. + */ + +var EXPORTED_SYMBOLS = ["FullscreenFrameChild"]; + +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..2d5404ab73 --- /dev/null +++ b/browser/base/content/test/fullscreen/browser.ini @@ -0,0 +1,24 @@ +[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.jsm +[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_newtab.js] +[browser_fullscreen_permissions_prompt.js] +[browser_fullscreen_warning.js] +support-files = fullscreen.html +[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..363a62fe28 --- /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.loadURI(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..5f765b81e8 --- /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..4207b2b521 --- /dev/null +++ b/browser/base/content/test/fullscreen/browser_fullscreen_api_fission.js @@ -0,0 +1,246 @@ +/* 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.jsm"; +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", + url: `http://example.com${fullscreenPath}`, + allow_fullscreen: true, + children: [ + { + name: "A", + url: `http://example.org${fullscreenPath}`, + allow_fullscreen: true, + children: [ + { + name: "C", + url: `http://example.com${fullscreenPath}`, + allow_fullscreen: true, + children: [ + { + name: "D", + url: `http://example.com${fullscreenPath}?different-uri=1`, + allow_fullscreen: true, + children: [ + { + name: "E", + url: `http://example.org${fullscreenPath}`, + allow_fullscreen: true, + children: [], + }, + ], + }, + ], + }, + ], + }, + { + name: "B", + 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: { + moduleURI: 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..b1199557b3 --- /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..914e2b262a --- /dev/null +++ b/browser/base/content/test/fullscreen/browser_fullscreen_enterInUrlbar.js @@ -0,0 +1,50 @@ +/* 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, + 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"); + }); +}); 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_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..210f65ee1c --- /dev/null +++ b/browser/base/content/test/fullscreen/browser_fullscreen_warning.js @@ -0,0 +1,127 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +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], + ], + }); + + 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"); + let warningShownPromise = BrowserTestUtils.waitForAttribute( + "onscreen", + warning, + "true" + ); + // 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; + } + ); +}); + +add_task(async function test_fullscreen_pointerlock_conflict() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["full-screen-api.enabled", true], + ["full-screen-api.allow-trusted-requests-only", false], + ], + }); + + await BrowserTestUtils.withNewTab("https://example.com", async browser => { + let fsWarning = document.getElementById("fullscreen-warning"); + let plWarning = document.getElementById("pointerlock-warning"); + + is( + fsWarning.getAttribute("onscreen"), + null, + "Should not show full screen warning initially." + ); + is( + plWarning.getAttribute("onscreen"), + null, + "Should not show pointer lock warning initially." + ); + + let fsWarningShownPromise = BrowserTestUtils.waitForAttribute( + "onscreen", + fsWarning, + "true" + ); + + info("Entering full screen and pointer lock."); + await SpecialPowers.spawn(browser, [], async () => { + await content.document.body.requestFullscreen(); + await content.document.body.requestPointerLock(); + }); + + await fsWarningShownPromise; + is( + fsWarning.getAttribute("onscreen"), + "true", + "Should show full screen warning." + ); + is( + plWarning.getAttribute("onscreen"), + null, + "Should not show pointer lock warning." + ); + + info("Exiting pointerlock"); + await SpecialPowers.spawn(browser, [], async () => { + await content.document.exitPointerLock(); + }); + + is( + fsWarning.getAttribute("onscreen"), + "true", + "Should still show full screen warning." + ); + is( + plWarning.getAttribute("onscreen"), + null, + "Should not show pointer lock warning." + ); + + // Cleanup + await document.exitFullscreen(); + }); +}); 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..ac53cac54c --- /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..b26527e917 --- /dev/null +++ b/browser/base/content/test/fullscreen/browser_fullscreen_window_open.js @@ -0,0 +1,70 @@ +/* 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() { + 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); + + await testExpectFullScreenExit(tab.linkedBrowser, true, async () => { + info("Calling window.open() again should reuse the existing window"); + jsWindowOpen(tab.linkedBrowser, true); + }); + + // 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(function test_parentWindowOpenExistWindow() { + return testWindowOpenExistingWindow(); +}); 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..d25c8d63d4 --- /dev/null +++ b/browser/base/content/test/fullscreen/head.js @@ -0,0 +1,149 @@ +const { ContentTaskUtils } = ChromeUtils.importESModule( + "resource://testing-common/ContentTaskUtils.sys.mjs" +); + +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; +} + +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..27a1c997c6 --- /dev/null +++ b/browser/base/content/test/fullscreen/open_and_focus_helper.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset='utf-8'> +</head> +<body> + <input></input> + <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'); + } + } + window.addEventListener('message', window.onMessage); + </script> +</body> +</html> |