tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

browser_fullscreen_api_fission.js (7373B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 /* This test checks that `document.fullscreenElement` is set correctly and
      5 * proper fullscreenchange events fire when an element inside of a
      6 * multi-origin tree of iframes calls `requestFullscreen()`. It is designed
      7 * to make sure the fullscreen API is working properly in fission when the
      8 * frame tree spans multiple processes.
      9 *
     10 * A similarly purposed Web Platform Test exists, but at the time of writing
     11 * is manual, so it cannot be run in CI:
     12 * `element-request-fullscreen-cross-origin-manual.sub.html`
     13 */
     14 
     15 "use strict";
     16 
     17 const actorModuleURI = getRootDirectory(gTestPath) + "FullscreenFrame.sys.mjs";
     18 const actorName = "FullscreenFrame";
     19 
     20 const fullscreenPath =
     21  getRootDirectory(gTestPath).replace("chrome://mochitests/content", "") +
     22  "fullscreen.html";
     23 
     24 const fullscreenTarget = "D";
     25 // TOP
     26 //  | \
     27 //  A  B
     28 //  |
     29 //  C
     30 //  |
     31 //  D
     32 //  |
     33 //  E
     34 const frameTree = {
     35  name: "TOP",
     36  // eslint-disable-next-line @microsoft/sdl/no-insecure-url
     37  url: `http://example.com${fullscreenPath}`,
     38  allow_fullscreen: true,
     39  children: [
     40    {
     41      name: "A",
     42      // eslint-disable-next-line @microsoft/sdl/no-insecure-url
     43      url: `http://example.org${fullscreenPath}`,
     44      allow_fullscreen: true,
     45      children: [
     46        {
     47          name: "C",
     48          // eslint-disable-next-line @microsoft/sdl/no-insecure-url
     49          url: `http://example.com${fullscreenPath}`,
     50          allow_fullscreen: true,
     51          children: [
     52            {
     53              name: "D",
     54              // eslint-disable-next-line @microsoft/sdl/no-insecure-url
     55              url: `http://example.com${fullscreenPath}?different-uri=1`,
     56              allow_fullscreen: true,
     57              children: [
     58                {
     59                  name: "E",
     60                  // eslint-disable-next-line @microsoft/sdl/no-insecure-url
     61                  url: `http://example.org${fullscreenPath}`,
     62                  allow_fullscreen: true,
     63                  children: [],
     64                },
     65              ],
     66            },
     67          ],
     68        },
     69      ],
     70    },
     71    {
     72      name: "B",
     73      // eslint-disable-next-line @microsoft/sdl/no-insecure-url
     74      url: `http://example.net${fullscreenPath}`,
     75      allow_fullscreen: true,
     76      children: [],
     77    },
     78  ],
     79 };
     80 
     81 add_task(async function test_fullscreen_api_cross_origin_tree() {
     82  await new Promise(r => {
     83    SpecialPowers.pushPrefEnv(
     84      {
     85        set: [
     86          ["full-screen-api.enabled", true],
     87          ["full-screen-api.allow-trusted-requests-only", false],
     88          ["full-screen-api.transition-duration.enter", "0 0"],
     89          ["full-screen-api.transition-duration.leave", "0 0"],
     90          ["dom.security.featurePolicy.header.enabled", true],
     91          ["dom.security.featurePolicy.webidl.enabled", true],
     92        ],
     93      },
     94      r
     95    );
     96  });
     97 
     98  // Register a custom window actor to handle tracking events
     99  // and constructing subframes
    100  ChromeUtils.registerWindowActor(actorName, {
    101    child: {
    102      esModuleURI: actorModuleURI,
    103      events: {
    104        fullscreenchange: { mozSystemGroup: true, capture: true },
    105        fullscreenerror: { mozSystemGroup: true, capture: true },
    106      },
    107    },
    108    allFrames: true,
    109  });
    110 
    111  let tab = await BrowserTestUtils.openNewForegroundTab({
    112    gBrowser,
    113    url: frameTree.url,
    114  });
    115 
    116  let frames = new Map();
    117  async function construct_frame_children(browsingContext, tree) {
    118    let actor = browsingContext.currentWindowGlobal.getActor(actorName);
    119    frames.set(tree.name, {
    120      browsingContext,
    121      actor,
    122    });
    123 
    124    for (let child of tree.children) {
    125      // Create the child IFrame and wait for it to load.
    126      let childBC = await actor.sendQuery("CreateChild", child);
    127      await construct_frame_children(childBC, child);
    128    }
    129  }
    130 
    131  await construct_frame_children(tab.linkedBrowser.browsingContext, frameTree);
    132 
    133  async function check_events(expected_events) {
    134    for (let [name, expected] of expected_events) {
    135      let actor = frames.get(name).actor;
    136 
    137      // Each content process fires the fullscreenchange
    138      // event independently and in parallel making it
    139      // possible for the promises returned by
    140      // `requestFullscreen` or `exitFullscreen` to
    141      // resolve before all events have fired. We wait
    142      // for the number of events to match before
    143      // continuing to ensure we don't miss an expected
    144      // event that hasn't fired yet.
    145      let events;
    146      await TestUtils.waitForCondition(async () => {
    147        events = await actor.sendQuery("GetEvents");
    148        return events.length == expected.length;
    149      }, `Waiting for number of events to match`);
    150 
    151      Assert.equal(events.length, expected.length, "Number of events equal");
    152      events.forEach((value, i) => {
    153        Assert.equal(value, expected[i], "Event type matches");
    154      });
    155    }
    156  }
    157 
    158  async function check_fullscreenElement(expected_elements) {
    159    for (let [name, expected] of expected_elements) {
    160      let element = await frames
    161        .get(name)
    162        .actor.sendQuery("GetFullscreenElement");
    163      Assert.equal(element, expected, "The fullScreenElement matches");
    164    }
    165  }
    166 
    167  // Trigger fullscreen from the target frame.
    168  let target = frames.get(fullscreenTarget);
    169  await target.actor.sendQuery("RequestFullscreen");
    170  // true is fullscreenchange and false is fullscreenerror.
    171  await check_events(
    172    new Map([
    173      ["TOP", [true]],
    174      ["A", [true]],
    175      ["B", []],
    176      ["C", [true]],
    177      ["D", [true]],
    178      ["E", []],
    179    ])
    180  );
    181  await check_fullscreenElement(
    182    new Map([
    183      ["TOP", "child_iframe"],
    184      ["A", "child_iframe"],
    185      ["B", "null"],
    186      ["C", "child_iframe"],
    187      ["D", "body"],
    188      ["E", "null"],
    189    ])
    190  );
    191 
    192  await target.actor.sendQuery("ExitFullscreen");
    193  // fullscreenchange should have fired on exit as well.
    194  // true is fullscreenchange and false is fullscreenerror.
    195  await check_events(
    196    new Map([
    197      ["TOP", [true, true]],
    198      ["A", [true, true]],
    199      ["B", []],
    200      ["C", [true, true]],
    201      ["D", [true, true]],
    202      ["E", []],
    203    ])
    204  );
    205  await check_fullscreenElement(
    206    new Map([
    207      ["TOP", "null"],
    208      ["A", "null"],
    209      ["B", "null"],
    210      ["C", "null"],
    211      ["D", "null"],
    212      ["E", "null"],
    213    ])
    214  );
    215 
    216  // Clear previous events before testing exiting fullscreen with ESC.
    217  for (const frame of frames.values()) {
    218    frame.actor.sendQuery("ClearEvents");
    219  }
    220  await target.actor.sendQuery("RequestFullscreen");
    221 
    222  // Escape should cause the proper events to fire and
    223  // document.fullscreenElement should be cleared.
    224  let finished_exiting = target.actor.sendQuery("WaitForChange");
    225  EventUtils.sendKey("ESCAPE");
    226  await finished_exiting;
    227  // true is fullscreenchange and false is fullscreenerror.
    228  await check_events(
    229    new Map([
    230      ["TOP", [true, true]],
    231      ["A", [true, true]],
    232      ["B", []],
    233      ["C", [true, true]],
    234      ["D", [true, true]],
    235      ["E", []],
    236    ])
    237  );
    238  await check_fullscreenElement(
    239    new Map([
    240      ["TOP", "null"],
    241      ["A", "null"],
    242      ["B", "null"],
    243      ["C", "null"],
    244      ["D", "null"],
    245      ["E", "null"],
    246    ])
    247  );
    248 
    249  // Remove the tests custom window actor.
    250  ChromeUtils.unregisterWindowActor("FullscreenFrame");
    251  BrowserTestUtils.removeTab(tab);
    252 });