Bug 1274919 - part4 : add telemetry probe to measure how long the cursor is hovering before opening the tab. draft
authorAlastor Wu <alwu@mozilla.com>
Thu, 20 Jul 2017 15:11:35 +0800
changeset 612000 7d0f9d7ac56d9769ad01ab710527188c410d0ca5
parent 611999 6e261b2a4560fb7cbef6436f94cc373ca690b24f
child 612001 ab936c745472ce7c34cc67088b8d82fb5beef8e7
push id69356
push useralwu@mozilla.com
push dateThu, 20 Jul 2017 07:11:52 +0000
bugs1274919
milestone56.0a1
Bug 1274919 - part4 : add telemetry probe to measure how long the cursor is hovering before opening the tab. Measure the time how long the cursor is hovering before opening the unselected tab. If the tab didn't be opened, the data won't be recorded. MozReview-Commit-ID: 4oTj0RzJhG
browser/base/content/tabbrowser.xml
toolkit/components/telemetry/Histograms.json
toolkit/content/widgets/browser.xml
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5579,16 +5579,17 @@
             // Also support adding event listeners (forward to the tab container)
             addEventListener(a, b, c) { this.self.tabContainer.addEventListener(a, b, c); },
             removeEventListener(a, b, c) { this.self.tabContainer.removeEventListener(a, b, c); }
           });
         ]]>
         </getter>
       </property>
       <field name="_soundPlayingAttrRemovalTimer">0</field>
+      <field name="_hoverTabTimer">null</field>
     </implementation>
 
     <handlers>
       <handler event="DOMWindowClose" phase="capturing">
         <![CDATA[
           if (!event.isTrusted)
             return;
 
@@ -7427,16 +7428,21 @@
           if (val)
             this.setAttribute("visuallyselected", "true");
           else
             this.removeAttribute("visuallyselected");
           this.parentNode.tabbrowser._tabAttrModified(this, ["visuallyselected"]);
 
           this._setPositionAttributes(val);
 
+          // Tab becomes visible, it's not unselected anymore.
+          if (val) {
+            this.finishUnselectedTabHoverTimer();
+          }
+
           return val;
           ]]>
         </setter>
       </property>
 
       <property name="_selected">
         <setter>
           <![CDATA[
@@ -7572,16 +7578,17 @@
               tabContainer._afterHoveredTab = candidate;
               candidate.setAttribute("afterhovered", "true");
             }
           }
 
           tabContainer._hoveredTab = this;
           if (this.linkedPanel && !this.selected) {
             this.linkedBrowser.unselectedTabHover(true);
+            this.startUnselectedTabHoverTimer();
           }
         ]]></body>
       </method>
 
       <method name="_mouseleave">
         <body><![CDATA[
           let tabContainer = this.parentNode;
           if (tabContainer._beforeHoveredTab) {
@@ -7591,16 +7598,62 @@
           if (tabContainer._afterHoveredTab) {
             tabContainer._afterHoveredTab.removeAttribute("afterhovered");
             tabContainer._afterHoveredTab = null;
           }
 
           tabContainer._hoveredTab = null;
           if (this.linkedPanel && !this.selected) {
             this.linkedBrowser.unselectedTabHover(false);
+            this.cancelUnselectedTabHoverTimer();
+          }
+        ]]></body>
+      </method>
+
+      <method name="startUnselectedTabHoverTimer">
+        <body><![CDATA[
+          // Only record data when we need to.
+          if (!this.linkedBrowser.shouldHandleUnselectedTabHover) {
+            return;
+          }
+
+          if (!TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
+            TelemetryStopwatch.start("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
+          }
+
+          if (this._hoverTabTimer) {
+            clearTimeout(this._hoverTabTimer);
+            this._hoverTabTimer = null;
+          }
+        ]]></body>
+      </method>
+
+      <method name="cancelUnselectedTabHoverTimer">
+        <body><![CDATA[
+          // Since we're listening "mouseout" event, instead of "mouseleave".
+          // Every time the cursor is moving from the tab to its child node (icon),
+          // it would dispatch "mouseout"(for tab) first and then dispatch
+          // "mouseover" (for icon, eg: close button, speaker icon) soon.
+          // It causes we would cancel present TelemetryStopwatch immediately
+          // when cursor is moving on the icon, and then start a new one.
+          // In order to avoid this situation, we could delay cancellation and
+          // remove it if we get "mouseover" within very short period.
+          this._hoverTabTimer = setTimeout(() => {
+            if (TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
+              TelemetryStopwatch.cancel("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
+            }
+          }, 100);
+        ]]></body>
+      </method>
+
+      <method name="finishUnselectedTabHoverTimer">
+        <body><![CDATA[
+          // Stop timer when the tab is opened.
+          if (TelemetryStopwatch.running("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this)) {
+            TelemetryStopwatch.finish("HOVER_UNTIL_UNSELECTED_TAB_OPENED", this);
           }
         ]]></body>
       </method>
 
       <method name="startMediaBlockTimer">
         <body><![CDATA[
           TelemetryStopwatch.start("TAB_MEDIA_BLOCKING_TIME_MS", this);
         ]]></body>
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -13606,10 +13606,21 @@
     "bug_numbers": [1368524],
     "expires_in_version": "60",
     "kind": "exponential",
     "low": 3,
     "high": 512,
     "n_buckets": 20,
     "keyed": true,
     "description": "Measures the number of milliseconds we spend synchronously notifying observers, keyed by topic. Note: only NotifyObservers calls which take over 500 microseconds are included in this probe."
+  },
+  "HOVER_UNTIL_UNSELECTED_TAB_OPENED": {
+    "record_in_processes": ["main"],
+    "alert_emails": ["alwu@mozilla.com"],
+    "expires_in_version": "60",
+    "kind": "exponential",
+    "high": 10000,
+    "n_buckets": 100,
+    "bug_numbers": [1274919],
+    "description": "Measure the time how long the cursor is hovering before opening the unselcted tab. Only record the data if someone requests for sending unselected tab hover msg.",
+    "releaseChannelCollection": "opt-out"
   }
 }
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -851,16 +851,19 @@
       </method>
 
       <!--
         Only send the message "Browser:UnselectedTabHover" when someone requests
         for the message, which can reduce non-necessary communication.
       -->
       <field name="_shouldSendUnselectedTabHover">false</field>
       <field name="_unselectedTabHoverMessageListenerCount">0</field>
+      <property name="shouldHandleUnselectedTabHover"
+                onget="return this._shouldSendUnselectedTabHover;"
+                readonly="true"/>
 
       <method name="unselectedTabHover">
         <parameter name="hovered"/>
         <body>
           <![CDATA[
             if (!this._shouldSendUnselectedTabHover) {
               return;
             }