Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

const PAGE_NON_ELIGIBLE_MEDIA =
// Import this in order to use `triggerPictureInPicture()`.
Services.scriptloader.loadSubScript(
this
);
// Bug 1673509 - This test requests a lot of fullscreen for media elements,
// which sometime Gecko would take longer time to fulfill.
requestLongerTimeout(2);
// This array contains the elements' id in `file_non_eligible_media.html`.
const gNonEligibleElementIds = [
"muted",
"volume-0",
"silent-audio-track",
"no-audio-track",
"short-duration",
"inaudible-captured-media",
];
/**
* This test is used to test couples of things about what kinds of media is
* eligible for being controlled by media control keys.
* (1) If media is inaudible all the time, then we would not control it.
* (2) If media starts inaudibly, we would not try to control it. But once it
* becomes audible later, we would keep controlling it until it's destroyed.
* (3) If media's duration is too short (<3s), then we would not control it.
*/
add_task(async function setupTestingPref() {
await SpecialPowers.pushPrefEnv({
set: [["media.mediacontrol.testingevents.enabled", true]],
});
});
add_task(
async function testNonAudibleMediaCantActivateControllerButAudibleMediaCan() {
for (const elementId of gNonEligibleElementIds) {
info(`open new tab with non eligible media elements`);
const tab = await createLoadedTabWrapper(PAGE_NON_ELIGIBLE_MEDIA, {
needCheck: couldElementBecomeEligible(elementId),
});
info(`although media is playing but it won't activate controller`);
await Promise.all([
startNonEligibleMedia(tab, elementId),
checkIfMediaIsStillPlaying(tab, elementId),
]);
ok(!tab.controller.isActive, "controller is still inactive");
if (couldElementBecomeEligible(elementId)) {
info(`make element ${elementId} audible would activate controller`);
await Promise.all([
makeElementEligible(tab, elementId),
checkOrWaitUntilControllerBecomeActive(tab),
]);
}
info(`remove tab`);
await tab.close();
}
}
);
/**
* Normally those media are not able to being controlled, however, once they
* enter fullsceen or Picture-in-Picture mode, then they can be controlled.
*/
add_task(async function testNonEligibleMediaEnterFullscreen() {
info(`open new tab with non eligible media elements`);
const tab = await createLoadedTabWrapper(PAGE_NON_ELIGIBLE_MEDIA);
for (const elementId of gNonEligibleElementIds) {
await startNonEligibleMedia(tab, elementId);
info(`entering fullscreen should activate the media controller`);
await enterFullScreen(tab, elementId);
await checkOrWaitUntilControllerBecomeActive(tab);
ok(true, `fullscreen ${elementId} media is able to being controlled`);
info(`leave fullscreen`);
await leaveFullScreen(tab);
}
info(`remove tab`);
await tab.close();
});
add_task(async function testNonEligibleMediaEnterPIPMode() {
info(`open new tab with non eligible media elements`);
const tab = await createLoadedTabWrapper(PAGE_NON_ELIGIBLE_MEDIA);
for (const elementId of gNonEligibleElementIds) {
await startNonEligibleMedia(tab, elementId);
info(`media entering PIP mode should activate the media controller`);
const winPIP = await triggerPictureInPicture(tab.linkedBrowser, elementId);
await checkOrWaitUntilControllerBecomeActive(tab);
ok(true, `PIP ${elementId} media is able to being controlled`);
info(`stop PIP mode`);
await BrowserTestUtils.closeWindow(winPIP);
}
info(`remove tab`);
await tab.close();
});
/**
* The following are helper functions.
*/
function startNonEligibleMedia(tab, elementId) {
return SpecialPowers.spawn(tab.linkedBrowser, [elementId], Id => {
const video = content.document.getElementById(Id);
if (!video) {
ok(false, `can't get the media element!`);
}
if (Id == "volume-0") {
video.volume = 0.0;
}
if (Id == "inaudible-captured-media") {
const context = new content.AudioContext();
context.createMediaElementSource(video);
}
info(`start non eligible media ${Id}`);
return video.play();
});
}
function checkIfMediaIsStillPlaying(tab, elementId) {
return SpecialPowers.spawn(tab.linkedBrowser, [elementId], Id => {
const video = content.document.getElementById(Id);
if (!video) {
ok(false, `can't get the media element!`);
}
return new Promise(r => {
// In order to test "media isn't affected by media control", we would not
// only check `mPaused`, we would also oberve "timeupdate" event multiple
// times to ensure that video is still playing continually.
let timeUpdateCount = 0;
ok(!video.paused);
video.ontimeupdate = () => {
if (++timeUpdateCount == 3) {
video.ontimeupdate = null;
r();
}
};
});
});
}
function couldElementBecomeEligible(elementId) {
return elementId == "muted" || elementId == "volume-0";
}
function makeElementEligible(tab, elementId) {
return SpecialPowers.spawn(tab.linkedBrowser, [elementId], Id => {
const video = content.document.getElementById(Id);
if (!video) {
ok(false, `can't get the media element!`);
}
// to turn inaudible media become audible in order to be controlled.
video.volume = 1.0;
video.muted = false;
});
}
function waitUntilMediaPaused(tab, elementId) {
return SpecialPowers.spawn(tab.linkedBrowser, [elementId], Id => {
const video = content.document.getElementById(Id);
if (!video) {
ok(false, `can't get the media element!`);
}
if (video.paused) {
ok(true, "media has been paused");
return Promise.resolve();
}
return new Promise(r => (video.onpaused = r));
});
}
function enterFullScreen(tab, elementId) {
return SpecialPowers.spawn(tab.linkedBrowser, [elementId], Id => {
return new Promise(r => {
const element = content.document.getElementById(Id);
element.requestFullscreen();
element.onfullscreenchange = () => {
element.onfullscreenchange = null;
element.onfullscreenerror = null;
r();
};
element.onfullscreenerror = () => {
// Retry until the element successfully enters fullscreen.
element.requestFullscreen();
};
});
});
}
function leaveFullScreen(tab) {
return SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
return content.document.exitFullscreen();
});
}