summaryrefslogtreecommitdiffstats
path: root/toolkit/components/pictureinpicture/tests/browser_improved_controls.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/pictureinpicture/tests/browser_improved_controls.js')
-rw-r--r--toolkit/components/pictureinpicture/tests/browser_improved_controls.js303
1 files changed, 303 insertions, 0 deletions
diff --git a/toolkit/components/pictureinpicture/tests/browser_improved_controls.js b/toolkit/components/pictureinpicture/tests/browser_improved_controls.js
new file mode 100644
index 0000000000..deadc8d53b
--- /dev/null
+++ b/toolkit/components/pictureinpicture/tests/browser_improved_controls.js
@@ -0,0 +1,303 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { TelemetryTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TelemetryTestUtils.sys.mjs"
+);
+
+const TEST_PAGE_LONG = TEST_ROOT + "test-video-selection.html";
+
+const IMPROVED_CONTROLS_ENABLED_PREF =
+ "media.videocontrols.picture-in-picture.improved-video-controls.enabled";
+
+async function getVideoCurrentTime(browser, videoID) {
+ return SpecialPowers.spawn(browser, [videoID], async videoID => {
+ return content.document.getElementById(videoID).currentTime;
+ });
+}
+
+async function getVideoDuration(browser, videoID) {
+ return SpecialPowers.spawn(browser, [videoID], async videoID => {
+ return content.document.getElementById(videoID).duration;
+ });
+}
+
+async function timestampUpdated(timestampEl, expectedTimestamp) {
+ await BrowserTestUtils.waitForMutationCondition(
+ timestampEl,
+ { childList: true },
+ () => {
+ return expectedTimestamp === timestampEl.textContent;
+ }
+ );
+}
+
+function checkTimeCloseEnough(actual, expected, message) {
+ let equal = Math.abs(actual - expected);
+ if (equal <= 0.5) {
+ is(equal <= 0.5, true, message);
+ } else {
+ is(actual, expected, message);
+ }
+}
+
+/**
+ * Tests the functionality of improved Picture-in-picture
+ * playback controls.
+ */
+add_task(async () => {
+ let videoID = "with-controls";
+
+ await BrowserTestUtils.withNewTab(
+ {
+ url: TEST_PAGE,
+ gBrowser,
+ },
+ async browser => {
+ let waitForVideoEvent = eventType => {
+ return BrowserTestUtils.waitForContentEvent(browser, eventType, true);
+ };
+
+ await ensureVideosReady(browser);
+ await SpecialPowers.spawn(browser, [videoID], async videoID => {
+ await content.document.getElementById(videoID).play();
+ });
+
+ await SpecialPowers.pushPrefEnv({
+ set: [[IMPROVED_CONTROLS_ENABLED_PREF, true]],
+ });
+
+ // Open the video in PiP
+ let pipWin = await triggerPictureInPicture(browser, videoID);
+ ok(pipWin, "Got Picture-in-Picture window.");
+
+ let fullscreenButton = pipWin.document.getElementById("fullscreen");
+ let seekForwardButton = pipWin.document.getElementById("seekForward");
+ let seekBackwardButton = pipWin.document.getElementById("seekBackward");
+
+ // Try seek forward button
+ let seekedForwardPromise = waitForVideoEvent("seeked");
+ EventUtils.synthesizeMouseAtCenter(seekForwardButton, {}, pipWin);
+ ok(await seekedForwardPromise, "The Forward button triggers");
+
+ // Try seek backward button
+ let seekedBackwardPromise = waitForVideoEvent("seeked");
+ EventUtils.synthesizeMouseAtCenter(seekBackwardButton, {}, pipWin);
+ ok(await seekedBackwardPromise, "The Backward button triggers");
+
+ // The Fullsreen button appears when the pref is enabled and the fullscreen hidden property is set to false
+ Assert.ok(!fullscreenButton.hidden, "The Fullscreen button is visible");
+
+ // The seek Forward button appears when the pref is enabled and the seek forward button hidden property is set to false
+ Assert.ok(!seekForwardButton.hidden, "The Forward button is visible");
+
+ // The seek Backward button appears when the pref is enabled and the seek backward button hidden property is set to false
+ Assert.ok(!seekBackwardButton.hidden, "The Backward button is visible");
+
+ // CLose the PIP window
+ let pipClosed = BrowserTestUtils.domWindowClosed(pipWin);
+ let closeButton = pipWin.document.getElementById("close");
+ EventUtils.synthesizeMouseAtCenter(closeButton, {}, pipWin);
+ await pipClosed;
+
+ await SpecialPowers.pushPrefEnv({
+ set: [[IMPROVED_CONTROLS_ENABLED_PREF, false]],
+ });
+
+ // Open the video in PiP
+ pipWin = await triggerPictureInPicture(browser, videoID);
+ ok(pipWin, "Got Picture-in-Picture window.");
+
+ fullscreenButton = pipWin.document.getElementById("fullscreen");
+ seekForwardButton = pipWin.document.getElementById("seekForward");
+ seekBackwardButton = pipWin.document.getElementById("seekBackward");
+
+ // The Fullsreen button disappears when the pref is disabled and the fullscreen hidden property is set to true
+ Assert.ok(
+ fullscreenButton.hidden,
+ "The Fullscreen button is not visible"
+ );
+
+ // The seek Forward button disappears when the pref is disabled and the seek forward button hidden property is set to true
+ Assert.ok(seekForwardButton.hidden, "The Forward button is not visible");
+
+ // The seek Backward button disappears when the pref is disabled and the seek backward button hidden property is set to true
+ Assert.ok(
+ seekBackwardButton.hidden,
+ "The Backward button is not visible"
+ );
+ }
+ );
+});
+
+/**
+ * Tests the functionality of Picture-in-picture
+ * video scrubber
+ */
+add_task(async function testVideoScrubber() {
+ let videoID = "long";
+
+ await BrowserTestUtils.withNewTab(
+ {
+ url: TEST_PAGE_LONG,
+ gBrowser,
+ },
+ async browser => {
+ await ensureVideosReady(browser);
+
+ await SpecialPowers.pushPrefEnv({
+ set: [[IMPROVED_CONTROLS_ENABLED_PREF, true]],
+ });
+
+ // Open the video in PiP
+ let pipWin = await triggerPictureInPicture(browser, videoID);
+ ok(pipWin, "Got Picture-in-Picture window.");
+
+ let scrubber = pipWin.document.getElementById("scrubber");
+ scrubber.focus();
+
+ let currentTime = await getVideoCurrentTime(browser, videoID);
+ let expectedVideoTime = 0;
+ const duration = await getVideoDuration(browser, videoID);
+ checkTimeCloseEnough(
+ currentTime,
+ expectedVideoTime,
+ "Video current time is 0"
+ );
+
+ let timestampEl = pipWin.document.getElementById("timestamp");
+ let expectedTimestamp = "0:00 / 0:08";
+
+ // Wait for the timestamp to update
+ await timestampUpdated(timestampEl, expectedTimestamp);
+ let actualTimestamp = timestampEl.textContent;
+ is(actualTimestamp, expectedTimestamp, "Timestamp reads 0:00 / 0:08");
+
+ EventUtils.synthesizeKey("KEY_ArrowRight", {}, pipWin);
+
+ currentTime = await getVideoCurrentTime(browser, videoID);
+ expectedVideoTime = 5;
+ checkTimeCloseEnough(
+ currentTime,
+ expectedVideoTime,
+ "Video current time is 5"
+ );
+
+ expectedTimestamp = "0:05 / 0:08";
+ await timestampUpdated(timestampEl, expectedTimestamp);
+ actualTimestamp = timestampEl.textContent;
+ is(actualTimestamp, expectedTimestamp, "Timestamp reads 0:05 / 0:08");
+
+ EventUtils.synthesizeKey("KEY_ArrowLeft", {}, pipWin);
+
+ currentTime = await getVideoCurrentTime(browser, videoID);
+ expectedVideoTime = 0;
+ checkTimeCloseEnough(
+ currentTime,
+ expectedVideoTime,
+ "Video current time is 0"
+ );
+
+ expectedTimestamp = "0:00 / 0:08";
+ await timestampUpdated(timestampEl, expectedTimestamp);
+ actualTimestamp = timestampEl.textContent;
+ is(actualTimestamp, expectedTimestamp, "Timestamp reads 0:00 / 0:08");
+
+ let rect = scrubber.getBoundingClientRect();
+
+ EventUtils.synthesizeMouse(
+ scrubber,
+ rect.width / 2,
+ rect.height / 2,
+ {},
+ pipWin
+ );
+
+ expectedVideoTime = duration / 2;
+ currentTime = await getVideoCurrentTime(browser, videoID);
+ checkTimeCloseEnough(
+ currentTime,
+ expectedVideoTime,
+ "Video current time is 3.98..."
+ );
+
+ expectedTimestamp = "0:04 / 0:08";
+ await timestampUpdated(timestampEl, expectedTimestamp);
+ actualTimestamp = timestampEl.textContent;
+ is(actualTimestamp, expectedTimestamp, "Timestamp reads 0:04 / 0:08");
+
+ EventUtils.synthesizeMouse(
+ scrubber,
+ rect.width / 2,
+ rect.height / 2,
+ { type: "mousedown" },
+ pipWin
+ );
+
+ EventUtils.synthesizeMouse(
+ scrubber,
+ rect.width,
+ rect.height / 2,
+ { type: "mousemove" },
+ pipWin
+ );
+
+ EventUtils.synthesizeMouse(
+ scrubber,
+ rect.width,
+ rect.height / 2,
+ { type: "mouseup" },
+ pipWin
+ );
+
+ expectedVideoTime = duration;
+ currentTime = await getVideoCurrentTime(browser, videoID);
+ checkTimeCloseEnough(
+ currentTime,
+ expectedVideoTime,
+ "Video current time is 7.96..."
+ );
+
+ await ensureMessageAndClosePiP(browser, videoID, pipWin, false);
+ }
+ );
+});
+
+/**
+ * Tests the behavior of the scrubber and position/duration indicator for a
+ * video with an invalid/non-finite duration.
+ */
+add_task(async function testInvalidDuration() {
+ await BrowserTestUtils.withNewTab(
+ {
+ url: TEST_PAGE_WITH_NAN_VIDEO_DURATION,
+ gBrowser,
+ },
+ async browser => {
+ const videoID = "nan-duration";
+
+ // This tests skips calling ensureVideosReady, because canplaythrough
+ // will never fire for the NaN duration video.
+
+ await SpecialPowers.pushPrefEnv({
+ set: [[IMPROVED_CONTROLS_ENABLED_PREF, true]],
+ });
+
+ // Open the video in PiP
+ let pipWin = await triggerPictureInPicture(browser, videoID);
+ ok(pipWin, "Got Picture-in-Picture window.");
+
+ // Both the scrubber and the duration should be hidden.
+ let timestampEl = pipWin.document.getElementById("timestamp");
+ ok(timestampEl.hidden, "Timestamp in the PIP window should be hidden.");
+
+ let scrubberEl = pipWin.document.getElementById("scrubber");
+ ok(
+ scrubberEl.hidden,
+ "Scrubber control in the PIP window should be hidden"
+ );
+ }
+ );
+});