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
--- 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;
}