Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* Any copyright is dedicated to the Public Domain.
"use strict";
const IMPROVED_CONTROLS_ENABLED_PREF =
"media.videocontrols.picture-in-picture.improved-video-controls.enabled";
const PLAYBACK_SPEED_ENABLED_PREF =
"media.videocontrols.picture-in-picture.playback-speed.enabled";
const videoID = "with-controls";
async function getVideoPlaybackRate(browser, id) {
return SpecialPowers.spawn(browser, [id], async videoID => {
return content.document.getElementById(videoID).playbackRate;
});
}
async function setVideoPlaybackRate(browser, id, rate) {
return SpecialPowers.spawn(browser, [id, rate], async (videoID, r) => {
content.document.getElementById(videoID).playbackRate = r;
});
}
function getReadoutRate(valueText) {
return JSON.parse(valueText.getAttribute("data-l10n-args")).rate;
}
/**
* Tests the playback speed controls in the Picture-in-Picture window:
* the originating rate is carried into the panel, panel controls drive the
* originating video, and changes made on the page sync back to the panel.
*/
add_task(async function test_playbackRate() {
clearSavedPosition();
await SpecialPowers.pushPrefEnv({
set: [
[IMPROVED_CONTROLS_ENABLED_PREF, true],
[PLAYBACK_SPEED_ENABLED_PREF, true],
],
});
await BrowserTestUtils.withNewTab(
{
url: TEST_PAGE,
gBrowser,
},
async browser => {
await ensureVideosReady(browser);
// Set a non-default rate on the page before opening PiP so we can verify
// it is carried over when the player window opens.
await setVideoPlaybackRate(browser, videoID, 1.5);
let pipWin = await triggerPictureInPicture(browser, videoID);
Assert.ok(pipWin, "Got Picture-in-Picture window.");
let doc = pipWin.document;
let playbackRateButton = doc.getElementById("playbackRate");
let panel = doc.getElementById("playbackRateSettings");
let slider = doc.getElementById("playback-rate-slider");
let valueText = doc.getElementById("playback-rate-value");
// The playback speed button is dropped from the end controls by a media
// query on narrow or short player windows, so size the window up before
// trying to click it. resizeTo fires no resize event when the window is
// already at least this size, so only resize when it is smaller.
let width = 640;
let height = 360;
if (pipWin.innerWidth < width || pipWin.innerHeight < height) {
let resizePromise = BrowserTestUtils.waitForEvent(pipWin, "resize");
pipWin.resizeTo(width, height);
await resizePromise;
}
await TestUtils.waitForCondition(
() => BrowserTestUtils.isVisible(playbackRateButton),
"Wait for the playback speed button to be visible."
);
Assert.equal(
parseFloat(slider.value),
1.5,
"Slider reflects the originating video's rate on open."
);
Assert.equal(
getReadoutRate(valueText),
1.5,
"Value readout reflects the rate."
);
// Opening the panel.
Assert.ok(BrowserTestUtils.isHidden(panel), "Panel starts hidden.");
// Retry the click: a synthesized click can still be delivered while the
// player window is settling, in which case it misses the button and the
// panel stays closed. Once a click lands the condition is true and no
// further click is sent, so this cannot toggle the panel back shut.
await TestUtils.waitForCondition(() => {
EventUtils.synthesizeMouseAtCenter(playbackRateButton, {}, pipWin);
return BrowserTestUtils.isVisible(panel);
}, "Wait for the panel to open on button click.");
// A preset button sets the originating video's rate.
let rateChangePromise = BrowserTestUtils.waitForContentEvent(
browser,
"ratechange",
true
);
let twoXPreset = panel.querySelector(
'.playback-rate-preset[data-rate="2"]'
);
EventUtils.synthesizeMouseAtCenter(twoXPreset, {}, pipWin);
await rateChangePromise;
Assert.equal(
await getVideoPlaybackRate(browser, videoID),
2,
"Preset sets the originating video's playback rate."
);
Assert.equal(
twoXPreset.getAttribute("aria-pressed"),
"true",
"The active preset is marked pressed."
);
// A rate change on the page syncs back into the panel.
await setVideoPlaybackRate(browser, videoID, 0.5);
await TestUtils.waitForCondition(
() => parseFloat(slider.value) === 0.5,
"Slider updates after the page changes the rate."
);
Assert.equal(
getReadoutRate(valueText),
0.5,
"Value readout updates from the page."
);
Assert.equal(
twoXPreset.getAttribute("aria-pressed"),
"false",
"The previously active preset is no longer marked pressed."
);
// The < and > keys step through the shortcut rate steps.
let speedUpPromise = BrowserTestUtils.waitForContentEvent(
browser,
"ratechange",
true
);
EventUtils.synthesizeKey(">", { shiftKey: true }, pipWin);
await speedUpPromise;
Assert.equal(
await getVideoPlaybackRate(browser, videoID),
0.75,
"The > key steps up to the next rate step."
);
await ensureMessageAndClosePiP(browser, videoID, pipWin, false);
// Don't leak the resized player window onto the next test.
clearSavedPosition();
}
);
});
/**
* Tests that the playback speed button stays hidden when the feature pref is
* disabled, even with the improved controls shown.
*/
add_task(async function test_playbackRate_pref_disabled() {
clearSavedPosition();
await SpecialPowers.pushPrefEnv({
set: [
[IMPROVED_CONTROLS_ENABLED_PREF, true],
[PLAYBACK_SPEED_ENABLED_PREF, false],
],
});
await BrowserTestUtils.withNewTab(
{
url: TEST_PAGE,
gBrowser,
},
async browser => {
await ensureVideosReady(browser);
let pipWin = await triggerPictureInPicture(browser, videoID);
Assert.ok(pipWin, "Got Picture-in-Picture window.");
let playbackRateButton = pipWin.document.getElementById("playbackRate");
Assert.ok(
playbackRateButton.hidden,
"Playback speed button is hidden when the pref is disabled."
);
// The shortcut keys are part of the feature, so they must stay inert too.
EventUtils.synthesizeKey(">", { shiftKey: true }, pipWin);
Assert.equal(
await getVideoPlaybackRate(browser, videoID),
1,
"The > key does not change the rate when the pref is disabled."
);
await ensureMessageAndClosePiP(browser, videoID, pipWin, false);
}
);
});