From 3326b6cc6888326fb03ed7e02ef6d6c41414042f Mon Sep 17 00:00:00 2001 From: Blake Winton Date: Wed, 4 May 2016 13:22:54 -0400 Subject: [PATCH] Add metrics. --- bootstrap.js | 9 ++++++- docs/metrics.md | 10 ++++---- utils.js | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ verticaltabs.jsm | 54 +++++++++++++++++++++++++++++++++++++++--- 4 files changed, 125 insertions(+), 9 deletions(-) diff --git a/bootstrap.js b/bootstrap.js index a116ca2e..f28f2dcd 100644 --- a/bootstrap.js +++ b/bootstrap.js @@ -79,6 +79,8 @@ function removeDefaultPrefs() { function install() { } +var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + function startup(data, reason) { // Load helpers from utils.js. include(data.resourceURI.spec + "utils.js"); @@ -97,12 +99,17 @@ function startup(data, reason) { Cu.import("resource://tabcenter/verticaltabs.jsm"); unload(vtInit()); watchWindows(function(window) { - let vt = new VerticalTabs(window); + let vt = new VerticalTabs(window, {newPayload, addPingStats}); unload(vt.unload.bind(vt), window); }, "navigator:browser"); + timer.initWithCallback({notify: () => { + sendPing(); + }}, 24*60*60*1000, Ci.nsITimer.TYPE_REPEATING_SLACK); // Every 24h. + // }}, 20*1000, Ci.nsITimer.TYPE_REPEATING_SLACK); // Every 20s for debugging. }; function shutdown(data, reason) { + sendPing(); if (reason == APP_SHUTDOWN) { return; } diff --git a/docs/metrics.md b/docs/metrics.md index bc782193..b9172962 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -99,7 +99,7 @@ Tab Center will record and report each of the following metrics: - Tab Center unpins - Tab Center contraction events - Tab Center expansion events - - Tab Center inaction contraction + - Tab Center inaction contraction (Version 2) This data will be used in three ways: @@ -127,14 +127,14 @@ An example payload (within the full Telemetry ping): “tabs_unpinned”: 4, “tab_center_pinned”: 1, “tab_center_unpinned”: 0, - “tab_center_expanded”: 9999, - “tab_center_teased”: 1234 + “tab_center_expanded”: 9999 + /* “tab_center_teased”: 1234 (Version 2) */ } } ``` And the schema we will use for Redshift: -```js +```sql local schema = { -- column name field type length attributes field name {"timestamp", "TIMESTAMP", nil, "SORTKEY", "Timestamp"}, @@ -149,7 +149,7 @@ local schema = { {"tab_center_pinned", "INTEGER", nil, nil, "payload[tab_center_pinned]"}, {"tab_center_unpinned", "INTEGER", nil, nil, "payload[tab_center_unpinned]"}, {"tab_center_expanded", "INTEGER", nil, nil, "payload[tab_center_expanded]"} - {"tab_center_teased", "INTEGER", nil, nil, "payload[tab_center_teased]"} +-- {"tab_center_teased", "INTEGER", nil, nil, "payload[tab_center_teased]"} (Version 2) } ``` diff --git a/utils.js b/utils.js index 1a72591b..34662ee9 100644 --- a/utils.js +++ b/utils.js @@ -149,3 +149,64 @@ function watchWindows(callback) { // Make sure to stop watching for windows if we're unloading unload(function() Services.ww.unregisterNotification(windowWatcher)); } + +const PAYLOAD_KEYS = [ + "tabs_created", + "tabs_destroyed", + "tabs_pinned", + "tabs_unpinned", + "tab_center_pinned", + "tab_center_unpinned", + "tab_center_expanded" +] + +function newPayload() { + let rv = {}; + PAYLOAD_KEYS.forEach(key => { + rv[key] = 0; + }); + return rv; +} + +var payload = newPayload(); + +function addPingStats(stats) { + PAYLOAD_KEYS.forEach(key => { + payload[key] += stats[key] || 0; + }); +} + +function sendPing() { + const observerService = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService); + // This looks strange, but it's required to send over the test ID. + const subject = { + wrappedJSObject: { + observersModuleSubjectWrapper: true, + object: 'tabcentertest1@mozilla.com' + } + }; + + let userAgent = Cc["@mozilla.org/network/protocol;1?name=http"] + .getService(Ci.nsIHttpProtocolHandler).userAgent; + + let windows = Services.wm.getEnumerator(null); + while (windows.hasMoreElements()) { + let vt = windows.getNext().VerticalTabs; + if (vt) { + vt.sendStats(); + } + } + + let ping = JSON.stringify({ + "test": "tabcentertest1@mozilla.com", // The em:id field from the add-on + "agent": userAgent, + "version": 1, // Just in case we need to drastically change the format later + "payload": payload + }); + payload = newPayload(); + // Send metrics to the main Test Pilot add-on. + // let console = (Components.utils.import("resource://gre/modules/devtools/Console.jsm", {})).console; + // console.log("Sending ping", ping); + observerService.notifyObservers(subject, 'testpilot::send-metric', ping); + // Clear out the metrics for next time… +} diff --git a/verticaltabs.jsm b/verticaltabs.jsm index 4a3c8397..ce62a816 100644 --- a/verticaltabs.jsm +++ b/verticaltabs.jsm @@ -89,10 +89,13 @@ function vtInit() { * * Main entry point of this add-on. */ -function VerticalTabs(window) { +function VerticalTabs(window, {newPayload, addPingStats}) { this.window = window; this.document = window.document; this.unloaders = []; + this.addPingStats = addPingStats; + this.newPayload = newPayload; + this.stats = this.newPayload(); this.init(); } VerticalTabs.prototype = { @@ -102,6 +105,9 @@ VerticalTabs.prototype = { this.unloaders.push(function() { delete this.window.VerticalTabs; }); + this.window.onunload = () => { + this.sendStats(); + } this.rearrangeXUL(); this.initContextMenu(); @@ -217,12 +223,21 @@ VerticalTabs.prototype = { "tooltiptext": "Keep sidebar open", "onclick": `let box = document.getElementById('main-window'); let newstate = box.getAttribute('tabspinned') == 'true' ? 'false' : 'true'; - box.setAttribute('tabspinned', newstate);` + box.setAttribute('tabspinned', newstate); + if (newstate == 'true') { + window.VerticalTabs.stats.tab_center_pinned++; + } else { + window.VerticalTabs.stats.tab_center_unpinned++; + } + ` }); toolbar.appendChild(pin_button); leftbox.insertBefore(toolbar, leftbox.firstChild); let enter = (event) => { + if (event.type === "mouseenter" && !tabs.expanded) { + this.stats.tab_center_expanded++; + } tabs.expanded = true; if (event.pageX <= 4) { leftbox.style.transition = "box-shadow 150ms ease-out, width 150ms ease-out"; @@ -249,6 +264,9 @@ VerticalTabs.prototype = { }); tabs.addEventListener("TabOpen", this, false); + tabs.addEventListener("TabClose", this, false); + tabs.addEventListener("TabPinned", this, false); + tabs.addEventListener("TabUnpinned", this, false); window.setTimeout(() => { if (mainWindow.getAttribute("tabspinned") === "true") { tabs.expanded = true; @@ -276,6 +294,9 @@ VerticalTabs.prototype = { tabs.tabbox.orient = "vertical"; // probably not necessary tabs.removeAttribute("width"); tabs.removeEventListener("TabOpen", this, false); + tabs.removeEventListener("TabClose", this, false); + tabs.removeEventListener("TabPinned", this, false); + tabs.removeEventListener("TabUnpinned", this, false); tabs.removeAttribute("vertical"); // Restore all individual tabs. @@ -350,6 +371,15 @@ VerticalTabs.prototype = { case "TabOpen": this.onTabOpen(aEvent); return; + case "TabClose": + this.onTabClose(aEvent); + return; + case "TabPinned": + this.onTabPinned(aEvent); + return; + case "TabUnpinned": + this.onTabUnpinned(aEvent); + return; case "mouseup": this.onMouseUp(aEvent); return; @@ -361,9 +391,22 @@ VerticalTabs.prototype = { onTabOpen: function(aEvent) { let tab = aEvent.target; + this.stats.tabs_created++; this.initTab(tab); }, + onTabClose: function(aEvent) { + this.stats.tabs_destroyed++; + }, + + onTabPinned: function(aEvent) { + this.stats.tabs_pinned++; + }, + + onTabUnpinned: function(aEvent) { + this.stats.tabs_unpinned++; + }, + onPopupShowing: function(aEvent) { if (!this.multiSelect) return; @@ -375,6 +418,11 @@ VerticalTabs.prototype = { } else { closeTabs.disabled = true; } - } + }, + + sendStats: function(payload) { + this.addPingStats(this.stats); + this.stats = this.newPayload(); + } };