Bug 1672401 - Picture-in-Picture description is displayed incorrectly on whereby.com. r=mconley,Gijs
authorChris Jackson <jack1391@msu.edu>
Mon, 09 Nov 2020 17:10:28 +0000
changeset 556473 1ecbca0ad84f726bc40f00811f502390040c2d58
parent 556472 834d6bf63d60ec2c005c3a188b5380e1a0661de8
child 556474 8ab8e3c5969deaed8e29e7ad9448b3229a2677bf
push id130535
push usermconley@mozilla.com
push dateMon, 09 Nov 2020 18:39:55 +0000
treeherderautoland@1ecbca0ad84f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley, Gijs
bugs1672401
milestone84.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1672401 - Picture-in-Picture description is displayed incorrectly on whereby.com. r=mconley,Gijs Differential Revision: https://phabricator.services.mozilla.com/D95166
toolkit/actors/PictureInPictureChild.jsm
toolkit/components/pictureinpicture/tests/browser.ini
toolkit/components/pictureinpicture/tests/browser_reversePiP.js
toolkit/components/pictureinpicture/tests/test-reversed.html
toolkit/themes/shared/media/videocontrols.css
--- a/toolkit/actors/PictureInPictureChild.jsm
+++ b/toolkit/actors/PictureInPictureChild.jsm
@@ -814,16 +814,24 @@ class PictureInPictureToggleChild extend
    * that is in the viewport.
    *
    * @param {Element} video The video the mouse is over.
    */
   onMouseOverVideo(video, event) {
     let oldOverVideo = this.getWeakOverVideo();
     let shadowRoot = video.openOrClosedShadowRoot;
 
+    if (video != oldOverVideo) {
+      if (video.getTransformToViewport().a == -1) {
+        shadowRoot.firstChild.setAttribute("flipped", true);
+      } else {
+        shadowRoot.firstChild.removeAttribute("flipped");
+      }
+    }
+
     // It seems from automated testing that if it's still very early on in the
     // lifecycle of a <video> element, it might not yet have a shadowRoot,
     // in which case, we can bail out here early.
     if (!shadowRoot) {
       if (oldOverVideo) {
         // We also clear the hover state on the old video we were hovering,
         // if there was one.
         this.onMouseLeaveVideo(oldOverVideo);
@@ -1372,16 +1380,22 @@ class PictureInPictureChild extends JSWi
     playerVideo.style.height = "100vh";
     playerVideo.style.width = "100vw";
     playerVideo.style.backgroundColor = "#000";
 
     doc.body.appendChild(playerVideo);
 
     originatingVideo.cloneElementVisually(playerVideo);
 
+    let shadowRoot = originatingVideo.openOrClosedShadowRoot;
+    if (originatingVideo.getTransformToViewport().a == -1) {
+      shadowRoot.firstChild.setAttribute("flipped", true);
+      playerVideo.style.transform = "scaleX(-1)";
+    }
+
     this.trackOriginatingVideo(originatingVideo);
 
     this.contentWindow.addEventListener(
       "unload",
       () => {
         let video = this.getWeakVideo();
         if (video) {
           this.untrackOriginatingVideo(video);
--- a/toolkit/components/pictureinpicture/tests/browser.ini
+++ b/toolkit/components/pictureinpicture/tests/browser.ini
@@ -6,16 +6,17 @@ support-files =
   test-opaque-overlay.html
   test-page.html
   test-page-with-iframe.html
   test-page-with-sound.html
   test-pointer-events-none.html
   test-transparent-overlay-1.html
   test-transparent-overlay-2.html
   test-video-selection.html
+  test-reversed.html
   test-video.mp4
   test-video-cropped.mp4
   test-video-vertical.mp4
   test-video-long.mp4
   short.mp4
   ../../../../dom/media/test/gizmo.mp4
   ../../../../dom/media/test/owl.mp3
 
@@ -42,16 +43,17 @@ skip-if = (os == "mac" && debug) || os =
 [browser_mouseButtonVariation.js]
 skip-if = debug
 [browser_noToggleOnAudio.js]
 [browser_playerControls.js]
 [browser_removeVideoElement.js]
 [browser_rerequestPiP.js]
 [browser_resizeVideo.js]
 skip-if = os == 'linux' # Bug 1594223
+[browser_reversePiP.js]
 [browser_saveLastPiPLoc.js]
 skip-if = os == 'linux' # Bug 1673465
 [browser_shortcutsAfterFocus.js]
 [browser_showMessage.js]
 [browser_smallVideoLayout.js]
 [browser_stripVideoStyles.js]
 [browser_tabIconOverlayPiP.js]
 [browser_thirdPartyIframe.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/pictureinpicture/tests/browser_reversePiP.js
@@ -0,0 +1,145 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ *  Tests that the PiP toggle button is not flipped
+ *  on certain websites (such as whereby.com).
+ */
+add_task(async () => {
+  await BrowserTestUtils.withNewTab(
+    {
+      gBrowser,
+      url: TEST_ROOT + "test-reversed.html",
+    },
+    async browser => {
+      await ensureVideosReady(browser);
+
+      let videoID = "reversed";
+
+      // Test the toggle button
+      await prepareForToggleClick(browser, videoID);
+
+      // Hover the mouse over the video to reveal the toggle.
+      await BrowserTestUtils.synthesizeMouseAtCenter(
+        `#${videoID}`,
+        {
+          type: "mousemove",
+        },
+        browser
+      );
+      await BrowserTestUtils.synthesizeMouseAtCenter(
+        `#${videoID}`,
+        {
+          type: "mouseover",
+        },
+        browser
+      );
+
+      let toggleFlippedAttribute = await SpecialPowers.spawn(
+        browser,
+        [videoID],
+        async videoID => {
+          let video = content.document.getElementById(videoID);
+          let shadowRoot = video.openOrClosedShadowRoot;
+          let controlsOverlay = shadowRoot.querySelector(".controlsOverlay");
+
+          await ContentTaskUtils.waitForCondition(() => {
+            return controlsOverlay.classList.contains("hovering");
+          }, "Waiting for the hovering state to be set on the video.");
+
+          return shadowRoot.firstChild.getAttribute("flipped");
+        }
+      );
+
+      // The "flipped" attribute should be set on the toggle button (when applicable).
+      Assert.equal(toggleFlippedAttribute, "true");
+    }
+  );
+});
+
+/**
+ * Tests that the "This video is playing in Picture-in-Picture" message
+ * as well as the video playing in PiP are both not flipped on certain sites
+ * (such as whereby.com)
+ */
+add_task(async () => {
+  await BrowserTestUtils.withNewTab(
+    {
+      gBrowser,
+      url: TEST_ROOT + "test-reversed.html",
+    },
+    async browser => {
+      /**
+       * A helper function used to get the "flipped" attribute of the video's shadowRoot's first child.
+       * @param {Element} browser The <xul:browser> hosting the <video>
+       * @param {String} videoID The ID of the video being checked
+       */
+      async function getFlippedAttribute(browser, videoID) {
+        let videoFlippedAttribute = await SpecialPowers.spawn(
+          browser,
+          [videoID],
+          async videoID => {
+            let video = content.document.getElementById(videoID);
+            let shadowRoot = video.openOrClosedShadowRoot;
+            return shadowRoot.firstChild.getAttribute("flipped");
+          }
+        );
+        return videoFlippedAttribute;
+      }
+
+      /**
+       * A helper function that returns the transform.a of the video being played in PiP.
+       * @param {Element} playerBrowser The <xul:browser> of the PiP window
+       */
+      async function getPiPVideoTransform(playerBrowser) {
+        let pipVideoTransform = await SpecialPowers.spawn(
+          playerBrowser,
+          [],
+          async () => {
+            let video = content.document.querySelector("video");
+            return video.getTransformToViewport().a;
+          }
+        );
+        return pipVideoTransform;
+      }
+
+      await ensureVideosReady(browser);
+
+      let videoID = "reversed";
+
+      let videoFlippedAttribute = await getFlippedAttribute(browser, videoID);
+      Assert.equal(videoFlippedAttribute, null); // The "flipped" attribute should not be set initially.
+
+      let pipWin = await triggerPictureInPicture(browser, videoID);
+
+      videoFlippedAttribute = await getFlippedAttribute(browser, "reversed");
+      Assert.equal(videoFlippedAttribute, "true"); // The "flipped" value should be set once the PiP window is opened (when applicable).
+
+      let playerBrowser = pipWin.document.getElementById("browser");
+      let pipVideoTransform = await getPiPVideoTransform(playerBrowser);
+      Assert.equal(pipVideoTransform, -1);
+
+      await ensureMessageAndClosePiP(browser, videoID, pipWin, false);
+
+      videoFlippedAttribute = await getFlippedAttribute(browser, "reversed");
+      Assert.equal(videoFlippedAttribute, null); // The "flipped" attribute should be removed after closing PiP.
+
+      // Now we want to test that regular (not-reversed) videos are unaffected
+      videoID = "not-reversed";
+      videoFlippedAttribute = await getFlippedAttribute(browser, videoID);
+      Assert.equal(videoFlippedAttribute, null);
+
+      pipWin = await triggerPictureInPicture(browser, videoID);
+
+      videoFlippedAttribute = await getFlippedAttribute(browser, videoID);
+      Assert.equal(videoFlippedAttribute, null);
+
+      playerBrowser = pipWin.document.getElementById("browser");
+      pipVideoTransform = await getPiPVideoTransform(playerBrowser);
+
+      await ensureMessageAndClosePiP(browser, videoID, pipWin, false);
+    }
+  );
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/pictureinpicture/tests/test-reversed.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Picture-in-Picture tests</title>
+  <script type="text/javascript" src="click-event-helper.js"></script>
+</head>
+<style>
+  #reversed {
+      transform: scaleX(-1);
+  }
+</style>
+<body>
+    <h1>Reversed video</h1>
+    <video id="reversed" src="test-video.mp4" controls loop="true" width="400" height="225"></video>
+    <h1>Not Reversed Video</h1>
+    <video id="not-reversed" src="test-video.mp4" loop="true" width="400" height="225"></video>
+</body>
+</html>
--- a/toolkit/themes/shared/media/videocontrols.css
+++ b/toolkit/themes/shared/media/videocontrols.css
@@ -15,16 +15,20 @@
   /* Prevent unwanted style inheritance. See bug 554717. */
   text-align: left;
   list-style-image: none !important;
   font: normal normal normal 100%/normal sans-serif !important;
   text-decoration: none !important;
   white-space: normal !important;
 }
 
+.videocontrols[flipped="true"] {
+  transform: scaleX(-1);
+}
+
 .controlsContainer {
   --clickToPlay-size: 48px;
   --button-size: 30px;
   --timer-size: 40px;
   --timer-long-size: 60px;
   --track-size: 5px;
   --thumb-size: 13px;
   --label-font-size: 13px;