/* eslint-env mozilla/frame-script */ /* eslint-disable mozilla/no-arbitrary-setTimeout */ "use strict"; var gMessageManager; function frameScript() { addMessageListener("Test:RequestFullscreen", () => { content.document.body.requestFullscreen(); }); addMessageListener("Test:ExitFullscreen", () => { content.document.exitFullscreen(); }); addMessageListener("Test:QueryFullscreenState", () => { sendAsyncMessage("Test:FullscreenState", { inDOMFullscreen: !!content.document.fullscreenElement, inFullscreen: content.fullScreen }); }); addMessageListener("Test:WaitActivated", () => { waitUntilActive(); }); content.document.addEventListener("fullscreenchange", () => { sendAsyncMessage("Test:FullscreenChanged", { inDOMFullscreen: !!content.document.fullscreenElement, inFullscreen: content.fullScreen }); }); function waitUntilActive() { if (docShell.isActive && content.document.hasFocus()) { sendAsyncMessage("Test:Activated"); } else { setTimeout(waitUntilActive, 10); } } waitUntilActive(); } function listenOneMessage(aMsg, aListener) { function listener({ data }) { gMessageManager.removeMessageListener(aMsg, listener); aListener(data); } gMessageManager.addMessageListener(aMsg, listener); } function listenOneEvent(aEvent, aListener) { function listener(evt) { removeEventListener(aEvent, listener); aListener(evt); } addEventListener(aEvent, listener); } function queryFullscreenState() { return new Promise(resolve => { listenOneMessage("Test:FullscreenState", resolve); gMessageManager.sendAsyncMessage("Test:QueryFullscreenState"); }); } function captureUnexpectedFullscreenChange() { ok(false, "catched an unexpected fullscreen change"); } const FS_CHANGE_DOM = 1 << 0; const FS_CHANGE_SIZE = 1 << 1; const FS_CHANGE_BOTH = FS_CHANGE_DOM | FS_CHANGE_SIZE; function waitForDocActivated() { return new Promise(resolve => { listenOneMessage("Test:Activated", resolve); gMessageManager.sendAsyncMessage("Test:WaitActivated"); }); } function waitForFullscreenChanges(aFlags) { return new Promise(resolve => { let fullscreenData = null; let sizemodeChanged = false; function tryResolve() { if ((!(aFlags & FS_CHANGE_DOM) || fullscreenData) && (!(aFlags & FS_CHANGE_SIZE) || sizemodeChanged)) { // In the platforms that support reporting occlusion state (e.g. Mac), // enter/exit fullscreen mode will trigger docshell being set to // non-activate and then set to activate back again. // For those platform, we should wait until the docshell has been // activated again, otherwise, the fullscreen request might be denied. waitForDocActivated().then(() => { if (!fullscreenData) { queryFullscreenState().then(resolve); } else { resolve(fullscreenData); } }); } } if (aFlags & FS_CHANGE_SIZE) { listenOneEvent("sizemodechange", () => { sizemodeChanged = true; tryResolve(); }); } if (aFlags & FS_CHANGE_DOM) { gMessageManager.removeMessageListener( "Test:FullscreenChanged", captureUnexpectedFullscreenChange); listenOneMessage("Test:FullscreenChanged", data => { gMessageManager.addMessageListener( "Test:FullscreenChanged", captureUnexpectedFullscreenChange); fullscreenData = data; tryResolve(); }); } }); } var gTests = [ { desc: "document method", affectsFullscreenMode: false, exitFunc: () => { gMessageManager.sendAsyncMessage("Test:ExitFullscreen"); } }, { desc: "escape key", affectsFullscreenMode: false, exitFunc: () => { executeSoon(() => EventUtils.synthesizeKey("KEY_Escape")); } }, { desc: "F11 key", affectsFullscreenMode: true, exitFunc() { executeSoon(() => EventUtils.synthesizeKey("KEY_F11")); } } ]; function checkState(expectedStates, contentStates) { is(contentStates.inDOMFullscreen, expectedStates.inDOMFullscreen, "The DOM fullscreen state of the content should match"); // TODO window.fullScreen is not updated as soon as the fullscreen // state flips in child process, hence checking it could cause // anonying intermittent failure. As we just want to confirm the // fullscreen state of the browser window, we can just check the // that on the chrome window below. // is(contentStates.inFullscreen, expectedStates.inFullscreen, // "The fullscreen state of the content should match"); is(!!document.fullscreenElement, expectedStates.inDOMFullscreen, "The DOM fullscreen state of the chrome should match"); is(window.fullScreen, expectedStates.inFullscreen, "The fullscreen state of the chrome should match"); } const kPage = "http://example.org/browser/browser/" + "base/content/test/general/dummy_page.html"; add_task(async function() { await pushPrefs( ["full-screen-api.transition-duration.enter", "0 0"], ["full-screen-api.transition-duration.leave", "0 0"]); registerCleanupFunction(async function() { if (window.fullScreen) { executeSoon(() => BrowserFullScreen()); await waitForFullscreenChanges(FS_CHANGE_SIZE); } }); let tab = await BrowserTestUtils.openNewForegroundTab({ gBrowser, url: kPage }); let browser = tab.linkedBrowser; gMessageManager = browser.messageManager; gMessageManager.loadFrameScript( "data:,(" + frameScript.toString() + ")();", false); gMessageManager.addMessageListener( "Test:FullscreenChanged", captureUnexpectedFullscreenChange); // Wait for the document being activated, so that // fullscreen request won't be denied. await new Promise(resolve => listenOneMessage("Test:Activated", resolve)); for (let test of gTests) { let contentStates; info("Testing exit DOM fullscreen via " + test.desc); contentStates = await queryFullscreenState(); checkState({inDOMFullscreen: false, inFullscreen: false}, contentStates); /* DOM fullscreen without fullscreen mode */ info("> Enter DOM fullscreen"); gMessageManager.sendAsyncMessage("Test:RequestFullscreen"); contentStates = await waitForFullscreenChanges(FS_CHANGE_BOTH); checkState({inDOMFullscreen: true, inFullscreen: true}, contentStates); info("> Exit DOM fullscreen"); test.exitFunc(); contentStates = await waitForFullscreenChanges(FS_CHANGE_BOTH); checkState({inDOMFullscreen: false, inFullscreen: false}, contentStates); /* DOM fullscreen with fullscreen mode */ info("> Enter fullscreen mode"); // Need to be asynchronous because sizemodechange event could be // dispatched synchronously, which would cause the event listener // miss that event and wait infinitely. executeSoon(() => BrowserFullScreen()); contentStates = await waitForFullscreenChanges(FS_CHANGE_SIZE); checkState({inDOMFullscreen: false, inFullscreen: true}, contentStates); info("> Enter DOM fullscreen in fullscreen mode"); gMessageManager.sendAsyncMessage("Test:RequestFullscreen"); contentStates = await waitForFullscreenChanges(FS_CHANGE_DOM); checkState({inDOMFullscreen: true, inFullscreen: true}, contentStates); info("> Exit DOM fullscreen in fullscreen mode"); test.exitFunc(); contentStates = await waitForFullscreenChanges( test.affectsFullscreenMode ? FS_CHANGE_BOTH : FS_CHANGE_DOM); checkState({ inDOMFullscreen: false, inFullscreen: !test.affectsFullscreenMode }, contentStates); /* Cleanup */ // Exit fullscreen mode if we are still in if (window.fullScreen) { info("> Cleanup"); executeSoon(() => BrowserFullScreen()); await waitForFullscreenChanges(FS_CHANGE_SIZE); } } BrowserTestUtils.removeTab(tab); });