Commit d7e08dcd authored by Mark Striemer's avatar Mark Striemer
Browse files

Bug 1591253 - Maintain PiP player position when source video is resized r=mconley

Keep the player window in a similar position to where it was before a resize.
The player window edge closest to a screen edge will be maintained in the X
and Y dimensions.

A video in the bottom right will have its bottom right corner stay in place,
while a video in the top left will have its top left corner stay in place.

Differential Revision: https://phabricator.services.mozilla.com/D52064

--HG--
extra : moz-landing-system : lando
parent a5495227
......@@ -435,6 +435,43 @@ var PictureInPicture = {
}
}
// Figure out where to position the window on screen. If we have a player
// window this will account for any change in video size. Otherwise the
// video will be positioned in the bottom right.
if (isPlayerWindow) {
// We might need to move the window to keep its positioning in a similar
// part of the screen.
//
// Find the distance from each edge of the screen of the old video, we'll
// keep the closest edge in the same spot.
let prevWidth = windowOrPlayer.innerWidth;
let prevHeight = windowOrPlayer.innerHeight;
let distanceLeft = windowOrPlayer.screenX;
let distanceRight =
screenWidth.value - windowOrPlayer.screenX - prevWidth;
let distanceTop = windowOrPlayer.screenY;
let distanceBottom =
screenHeight.value - windowOrPlayer.screenY - prevHeight;
let left = windowOrPlayer.screenX;
let top = windowOrPlayer.screenY;
if (distanceRight < distanceLeft) {
// Closer to the right edge than the left. Move the window right by
// the difference in the video widths.
left += prevWidth - width;
}
if (distanceBottom < distanceTop) {
// Closer to the bottom edge than the top. Move the window down by
// the difference in the video heights.
top += prevHeight - height;
}
return { top, left, width, height };
}
// Now that we have the dimensions of the video, we need to figure out how
// to position it in the bottom right corner. Since we know the width of the
// available rect, we need to subtract the dimensions of the window we're
......@@ -465,8 +502,9 @@ var PictureInPicture = {
return;
}
let { width, height } = this.fitToScreen(win, videoData);
let { top, left, width, height } = this.fitToScreen(win, videoData);
win.resizeTo(width, height);
win.moveTo(left, top);
},
openToggleContextMenu(window, data) {
......
......@@ -4,21 +4,27 @@
"use strict";
/**
* Tests that if a <video> element is resized the Picture-in-Picture window
* will be resized to match the new dimensions.
* Run the resize test on a player window.
*
* @param browser (xul:browser)
* The browser that has the source video.
*
* @param videoID (string)
* The id of the video in the browser to test.
*
* @param pipWin (player window)
* A player window to run the tests on.
*
* @param opts (object)
* The options for the test.
*
* pinX (boolean):
* If true, the video's X position shouldn't change when resized.
*
* pinY (boolean):
* If true, the video's Y position shouldn't change when resized.
*/
add_task(async () => {
for (let videoID of ["with-controls", "no-controls"]) {
info(`Testing ${videoID} case.`);
await BrowserTestUtils.withNewTab(
{
url: TEST_PAGE,
gBrowser,
},
async browser => {
let pipWin = await triggerPictureInPicture(browser, videoID);
async function testVideo(browser, videoID, pipWin, { pinX, pinY } = {}) {
async function switchVideoSource(src) {
let videoResized = BrowserTestUtils.waitForEvent(pipWin, "resize");
await ContentTask.spawn(
......@@ -33,6 +39,54 @@ add_task(async () => {
await videoResized;
}
/**
* Check the new screen position against the previous one. When
* pinX or pinY is true then the top left corner is checked in that
* dimension. Otherwise, the bottom right corner is checked.
*
* The video position is determined by the screen edge it's closest
* to, so in the default bottom right its bottom right corner should
* match the previous video's bottom right corner. For the top left,
* the top left corners should match.
*/
function checkPosition(
previousScreenX,
previousScreenY,
previousWidth,
previousHeight,
newScreenX,
newScreenY,
newWidth,
newHeight
) {
if (pinX) {
Assert.equal(
previousScreenX,
newScreenX,
"New video is still in the same X position"
);
} else {
Assert.equal(
previousScreenX + previousWidth,
newScreenX + newWidth,
"New video ends at the same screen X position"
);
}
if (pinY) {
Assert.equal(
previousScreenY,
newScreenY,
"New video is still in the same Y position"
);
} else {
Assert.equal(
previousScreenY + previousHeight,
newScreenY + newHeight,
"New video ends at the same screen Y position"
);
}
}
Assert.ok(pipWin, "Got PiP window.");
let initialWidth = pipWin.innerWidth;
......@@ -44,6 +98,10 @@ add_task(async () => {
"Original aspect ratio is 16:9"
);
// Store the window position for later.
let initialScreenX = pipWin.mozInnerScreenX;
let initialScreenY = pipWin.mozInnerScreenY;
await switchVideoSource("test-video-cropped.mp4");
let resizedWidth = pipWin.innerWidth;
......@@ -54,39 +112,57 @@ add_task(async () => {
133, // 4 / 3 = 1.333333333
"Resized aspect ratio is 4:3"
);
Assert.equal(
Assert.equal(initialWidth, resizedWidth, "Resized video has the same width");
Assert.greater(resizedHeight, initialHeight, "Resized video grew vertically");
let resizedScreenX = pipWin.mozInnerScreenX;
let resizedScreenY = pipWin.mozInnerScreenY;
checkPosition(
initialScreenX,
initialScreenY,
initialWidth,
resizedWidth,
"Resized video has the same width"
);
Assert.greater(
resizedHeight,
initialHeight,
"Resized video grew vertically"
resizedScreenX,
resizedScreenY,
resizedWidth,
resizedHeight
);
await switchVideoSource("test-video-vertical.mp4");
let verticalAspectRatio = pipWin.innerWidth / pipWin.innerHeight;
let verticalWidth = pipWin.innerWidth;
let verticalHeight = pipWin.innerHeight;
let verticalAspectRatio = verticalWidth / verticalHeight;
Assert.equal(
Math.floor(verticalAspectRatio * 100),
50, // 1 / 2 = 0.5
"Vertical aspect ratio is 1:2"
);
Assert.less(
pipWin.innerWidth,
resizedWidth,
"Vertical video width shrunk"
);
Assert.less(verticalWidth, resizedWidth, "Vertical video width shrunk");
Assert.equal(
resizedWidth,
pipWin.innerHeight,
verticalHeight,
"Vertical video height matches previous width"
);
let verticalScreenX = pipWin.mozInnerScreenX;
let verticalScreenY = pipWin.mozInnerScreenY;
checkPosition(
resizedScreenX,
resizedScreenY,
resizedWidth,
resizedHeight,
verticalScreenX,
verticalScreenY,
verticalWidth,
verticalHeight
);
await switchVideoSource("test-video.mp4");
let restoredAspectRatio = pipWin.innerWidth / pipWin.innerHeight;
let restoredWidth = pipWin.innerWidth;
let restoredHeight = pipWin.innerHeight;
let restoredAspectRatio = restoredWidth / restoredHeight;
Assert.equal(
Math.floor(restoredAspectRatio * 100),
177,
......@@ -103,8 +179,71 @@ add_task(async () => {
"Restored video has its original height"
);
let restoredScreenX = pipWin.mozInnerScreenX;
let restoredScreenY = pipWin.mozInnerScreenY;
checkPosition(
initialScreenX,
initialScreenY,
initialWidth,
initialHeight,
restoredScreenX,
restoredScreenY,
restoredWidth,
restoredHeight
);
}
/**
* Tests that if a <video> element is resized the Picture-in-Picture window
* will be resized to match the new dimensions.
*/
add_task(async () => {
for (let videoID of ["with-controls", "no-controls"]) {
info(`Testing ${videoID} case.`);
await BrowserTestUtils.withNewTab(
{
url: TEST_PAGE,
gBrowser,
},
async browser => {
let pipWin = await triggerPictureInPicture(browser, videoID);
await testVideo(browser, videoID, pipWin);
pipWin.moveTo(0, 0);
await testVideo(browser, videoID, pipWin, { pinX: true, pinY: true });
await BrowserTestUtils.closeWindow(pipWin);
}
);
}
});
/**
* Tests that the RTL video starts on the left and is pinned in the X dimension.
*/
add_task(async () => {
await SpecialPowers.pushPrefEnv({ set: [["intl.l10n.pseudo", "bidi"]] });
for (let videoID of ["with-controls", "no-controls"]) {
info(`Testing ${videoID} case.`);
await BrowserTestUtils.withNewTab(
{
url: TEST_PAGE,
gBrowser,
},
async browser => {
let pipWin = await triggerPictureInPicture(browser, videoID);
await testVideo(browser, videoID, pipWin, { pinX: true });
await BrowserTestUtils.closeWindow(pipWin);
}
);
}
await SpecialPowers.popPrefEnv();
});
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment