summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/fullscreen/browser_fullscreen_api_fission.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/test/fullscreen/browser_fullscreen_api_fission.js')
-rw-r--r--browser/base/content/test/fullscreen/browser_fullscreen_api_fission.js252
1 files changed, 252 insertions, 0 deletions
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);
+});