forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			252 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			252 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* 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);
 | |
| });
 | 
