forked from mirrors/gecko-dev
This adds an additional action that is triggered when the fullscreen transition event is received. That interim action is used to re-focus the original tab. Without this change, an asynchronous fullscreen transition will silently swallow the MozAfterPaint event that is necessary to detect the end of fullscreen. Before this change, with asynchronous transitions, here's the flow: 1) The test opens Tab 1 which opens Tab 2. 2) Tab 1 enters fullscreen. This transition takes awhile but signals success early. This is intentional because we want web content to be able to be fully layed out when the fullscreen transition is complete. 3) Thinking that it has reached fullscreen, the test focuses Tab 2, which causes a fullscreen exit transition to be queued up but not yet run. Then Tab 1 is unfocused and a MozAfterPaint message is sent, but nobody is listening for it so it has no effect. If the fullscreen transition was synchronous, the actions of Step 6 (below) would happen before the change in focus and there would be an event listener ready for the MozAfterPaint event. 4) The test waits for the DOMFullscreenChild to send the DOMFullscreen:Painted message, which it will do when it receives a MozAfterPaint event. 5) Enter fullscreen transition completes, exit fullscreen transition starts. 6) The MozDOMFullscreen:Exited event is sent to the DOMFullscreenChild, which starts listening for the MozAfterPaint event. This event has already been sent. 7) Test times out. This change adds an additional action to take place between steps 6 and 7. That additional action refocuses Tab 1. This makes it send another MozAfterPaint event which the DOMFullscreenChild is ready to receive. Differential Revision: https://phabricator.services.mozilla.com/D180884
172 lines
5.1 KiB
JavaScript
172 lines
5.1 KiB
JavaScript
const TEST_URL =
|
|
"https://example.com/browser/browser/base/content/test/fullscreen/open_and_focus_helper.html";
|
|
|
|
function waitForFullScreenState(browser, state, actionAfterFSEvent) {
|
|
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;
|
|
if (actionAfterFSEvent) {
|
|
actionAfterFSEvent();
|
|
}
|
|
},
|
|
{ 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,
|
|
actionAfterFSEvent
|
|
) {
|
|
let fsPromise = waitForFullScreenState(browser, false, actionAfterFSEvent);
|
|
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;
|
|
}
|
|
|
|
async function jsClickLink(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 click a link
|
|
await content.wrappedJSObject.sendMessage(destWin, "clicklink");
|
|
});
|
|
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 }
|
|
);
|
|
}
|
|
});
|
|
}
|