Bug 1513600 - Use elementFromPoint() to measure isMouseOverVideo. r=jaws, a=RyanVM
authorTimothy Guan-tin Chien <timdream@gmail.com>
Sat, 15 Dec 2018 02:56:27 +0000
changeset 509100 dd09f113cb8a6936dd8d92b7b5a425eb43019af0
parent 509099 1a45a417a33af7317ff0be891363dacdbdf98b36
child 509101 b3837d6171d2c848d82fc7de31c0e84d9eacff14
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws, RyanVM
bugs1513600, 1493525
milestone65.0
Bug 1513600 - Use elementFromPoint() to measure isMouseOverVideo. r=jaws, a=RyanVM The checkEventWithin method is broken by two bugs: The first one is bug 1493525 because we ended up pass the proxy instance, instead of the element reference, as the parent node to compare. The second one is unknown and happened sometime after that bug. The |relatedTarget| of the mouse event is always <video>, instead of the element within Shadow DOM that the cursor is moving out to. Instead of identify the second bug in the DOM, this patch employs a simpler fix by using elementFromPoint() to identify the cursor position. Differential Revision: https://phabricator.services.mozilla.com/D14342
toolkit/content/widgets/videocontrols.js
--- a/toolkit/content/widgets/videocontrols.js
+++ b/toolkit/content/widgets/videocontrols.js
@@ -1005,28 +1005,43 @@ this.VideoControlsImplWidget = class {
       _hideControlsFn() {
         if (!this.scrubber.isDragging) {
           this.startFade(this.controlBar, false);
           this._hideControlsTimeout = 0;
           this._controlsHiddenByTimeout = true;
         }
       },
       HIDE_CONTROLS_TIMEOUT_MS: 2000,
+
+      // By "Video" we actually mean the video controls container,
+      // because we don't want to consider the padding of <video> added
+      // by the web content.
+      isMouseOverVideo(event) {
+        // XXX: this triggers reflow too, but the layout should only be dirty
+        // if the web content touches it while the mouse is moving.
+        let el = this.shadowRoot.elementFromPoint(event.clientX, event.clientY);
+
+        // As long as this is not null, the cursor is over something within our
+        // Shadow DOM.
+        return !!el;
+      },
+
       isMouseOverControlBar(event) {
         // XXX: this triggers reflow too, but the layout should only be dirty
         // if the web content touches it while the mouse is moving.
         let el = this.shadowRoot.elementFromPoint(event.clientX, event.clientY);
         while (el && el !== this.shadowRoot) {
           if (el == this.controlBar) {
             return true;
           }
           el = el.parentNode;
         }
         return false;
       },
+
       onMouseMove(event) {
         // If the controls are static, don't change anything.
         if (!this.dynamicControls) {
           return;
         }
 
         this.window.clearTimeout(this._hideControlsTimeout);
 
@@ -1062,37 +1077,27 @@ this.VideoControlsImplWidget = class {
       onMouseInOut(event) {
         // If the controls are static, don't change anything.
         if (!this.dynamicControls) {
           return;
         }
 
         this.window.clearTimeout(this._hideControlsTimeout);
 
-        // Ignore events caused by transitions between child nodes.
-        // Note that the videocontrols element is the same
-        // size as the *content area* of the video element,
-        // but this is not the same as the video element's
-        // border area if the video has border or padding.
-        if (this.checkEventWithin(event, this.videocontrols)) {
-          return;
-        }
-
-        var isMouseOver = (event.type == "mouseover");
-        var isMouseInControls = this.isMouseOverControlBar(event);
+        let isMouseOverVideo = this.isMouseOverVideo(event);
 
         // Suppress fading out the controls until the video has rendered
         // its first frame. But since autoplay videos start off with no
         // controls, let them fade-out so the controls don't get stuck on.
-        if (!this.firstFrameShown && !isMouseOver &&
+        if (!this.firstFrameShown && !isMouseOverVideo &&
             !this.video.autoplay) {
           return;
         }
 
-        if (!isMouseOver && !isMouseInControls) {
+        if (!isMouseOverVideo && !this.isMouseOverControlBar(event)) {
           this.adjustControlSize();
 
           // Keep the controls visible if the click-to-play is visible.
           if (!this.clickToPlay.hidden) {
             return;
           }
 
           this.startFadeOut(this.controlBar, false);
@@ -1724,29 +1729,16 @@ this.VideoControlsImplWidget = class {
 
         for (let tt of this.overlayableTextTracks) {
           this.addNewTextTrack(tt);
         }
 
         this.setClosedCaptionButtonState();
       },
 
-      checkEventWithin(event, parent1, parent2) {
-        function isDescendant(node) {
-          while (node) {
-            if (node == parent1 || node == parent2) {
-              return true;
-            }
-            node = node.parentNode;
-          }
-          return false;
-        }
-        return isDescendant(event.target) && isDescendant(event.relatedTarget);
-      },
-
       log(msg) {
         if (this.debug) {
           this.window.console.log("videoctl: " + msg + "\n");
         }
       },
 
       get isTopLevelSyntheticDocument() {
         return this.document.mozSyntheticDocument && this.window === this.window.top;