Bug 866642 - We need to add general telemetry / FHR info to devtools r=jwalker

This commit is contained in:
Michael Ratcliffe 2013-05-24 11:26:17 +01:00
parent c5c9423f34
commit d70b929bd9
17 changed files with 955 additions and 33 deletions

View File

@ -1157,6 +1157,11 @@ pref("devtools.editor.component", "orion");
// Enable the Font Inspector
pref("devtools.fontinspector.enabled", true);
// Pref to store the browser version at the time of a telemetry ping for an
// opened developer tool. This allows us to ping telemetry just once per browser
// version for each user.
pref("devtools.telemetry.tools.opened.version", "{}");
// Whether the character encoding menu is under the main Firefox button. This
// preference is a string so that localizers can alter it.
pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.properties");

View File

@ -19,6 +19,10 @@ Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/devtools/gcli.jsm");
Cu.import("resource:///modules/devtools/shared/event-emitter.js");
var require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
let Telemetry = require("devtools/shared/telemetry");
let telemetry = new Telemetry();
XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
"resource:///modules/devtools/gDevTools.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "devtools",
@ -1950,8 +1954,7 @@ gcli.addCommand({
description: gcli.lookup('paintflashingToggleDesc'),
manual: gcli.lookup('paintflashingManual'),
exec: function(args, context) {
var gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
var window = gBrowser.contentWindow;
var window = context.environment.window;
var wUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils);
wUtils.paintFlashing = !wUtils.paintFlashing;
@ -1970,6 +1973,15 @@ gcli.addCommand({
var target = devtools.TargetFactory.forTab(tab);
target.off("navigate", fireChange);
target.once("navigate", fireChange);
var window = context.environment.window;
var wUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
if (wUtils.paintFlashing) {
telemetry.toolOpened("paintflashing");
} else {
telemetry.toolClosed("paintflashing");
}
}
}(this));

View File

@ -15,6 +15,9 @@ const CHROME_DEBUGGER_PROFILE_NAME = "-chrome-debugger";
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
let Telemetry = require("devtools/shared/telemetry");
XPCOMUtils.defineLazyModuleGetter(this,
"DebuggerServer", "resource://gre/modules/devtools/dbg-server.jsm");
@ -395,6 +398,7 @@ this.ChromeDebuggerProcess = function ChromeDebuggerProcess(aDebuggerUI, aOnClos
this._win = aDebuggerUI.chromeWindow;
this._closeCallback = aOnClose;
this._runCallback = aOnRun;
this._telemetry = new Telemetry();
this._initServer();
this._initProfile();
@ -477,6 +481,8 @@ ChromeDebuggerProcess.prototype = {
process.runwAsync(args, args.length, { observe: this.close.bind(this) });
this._dbgProcess = process;
this._telemetry.toolOpened("jsbrowserdebugger");
if (typeof this._runCallback == "function") {
this._runCallback.call({}, this);
}
@ -487,6 +493,9 @@ ChromeDebuggerProcess.prototype = {
*/
close: function() {
dumpn("Closing chrome debugging process");
this._telemetry.toolClosed("jsbrowserdebugger");
if (!this.globalUI) {
dumpn("globalUI is missing");
return;

View File

@ -440,18 +440,35 @@ let gDevToolsBrowser = {
let amp = doc.getElementById("appmenu_webDeveloper_popup");
if (amp) {
let ref = (prevDef != null) ?
doc.getElementById("appmenuitem_" + prevDef.id).nextSibling :
doc.getElementById("appmenu_devtools_separator");
let ref;
amp.insertBefore(elements.appmenuitem, ref);
if (prevDef != null) {
let menuitem = doc.getElementById("appmenuitem_" + prevDef.id);
ref = menuitem && menuitem.nextSibling ? menuitem.nextSibling : null;
} else {
ref = doc.getElementById("appmenu_devtools_separator");
}
if (ref) {
amp.insertBefore(elements.appmenuitem, ref);
}
}
let mp = doc.getElementById("menuWebDeveloperPopup");
let ref = (prevDef != null) ?
doc.getElementById("menuitem_" + prevDef.id).nextSibling :
doc.getElementById("menu_devtools_separator");
mp.insertBefore(elements.menuitem, ref);
if (mp) {
let ref;
if (prevDef != null) {
let menuitem = doc.getElementById("menuitem_" + prevDef.id);
ref = menuitem && menuitem.nextSibling ? menuitem.nextSibling : null;
} else {
ref = doc.getElementById("menu_devtools_separator");
}
if (ref) {
mp.insertBefore(elements.menuitem, ref);
}
}
}
},

View File

@ -6,6 +6,7 @@
var Promise = require("sdk/core/promise");
var EventEmitter = require("devtools/shared/event-emitter");
var Telemetry = require("devtools/shared/telemetry");
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
@ -28,6 +29,8 @@ function ToolSidebar(tabbox, panel, showTabstripe=true)
this._panelDoc = this._tabbox.ownerDocument;
this._toolPanel = panel;
this._telemetry = new Telemetry();
this._tabbox.tabpanels.addEventListener("select", this, true);
this._tabs = new Map();
@ -137,9 +140,11 @@ ToolSidebar.prototype = {
let previousTool = this._currentTool;
this._currentTool = this.getCurrentTabID();
if (previousTool) {
this._telemetry.toolClosed(previousTool);
this.emit(previousTool + "-unselected");
}
this._telemetry.toolOpened(this._currentTool);
this.emit(this._currentTool + "-selected");
this.emit("select", this._currentTool);
}
@ -201,6 +206,10 @@ ToolSidebar.prototype = {
this._tabbox.tabs.removeChild(this._tabbox.tabs.firstChild);
}
if (this._currentTool) {
this._telemetry.toolClosed(this._currentTool);
}
this._tabs = null;
this._tabbox = null;
this._panelDoc = null;

View File

@ -55,7 +55,13 @@ function testMouseClicks() {
}
gDevTools.once("pref-changed", prefChanged);
info("Click event synthesized for index " + index);
EventUtils.synthesizeMouse(prefNodes[index], 10, 10, {}, panelWin);
prefNodes[index].scrollIntoView();
// We use executeSoon here to ensure that the element is in view and
// clickable.
executeSoon(function() {
EventUtils.synthesizeMouseAtCenter(prefNodes[index], {}, panelWin);
});
}
function prefChanged(event, data) {
@ -89,11 +95,11 @@ function checkTools() {
function toggleTools() {
if (index < prefNodes.length) {
gDevTools.once("tool-unregistered", checkUnregistered);
EventUtils.synthesizeMouse(prefNodes[index], 10, 10, {}, panelWin);
EventUtils.synthesizeMouseAtCenter(prefNodes[index], {}, panelWin);
}
else if (index < 2*prefNodes.length) {
gDevTools.once("tool-registered", checkRegistered);
EventUtils.synthesizeMouse(prefNodes[index - prefNodes.length], 10, 10, {}, panelWin);
EventUtils.synthesizeMouseAtCenter(prefNodes[index - prefNodes.length], {}, panelWin);
}
else {
cleanup();

View File

@ -8,6 +8,7 @@ const {Cc, Ci, Cu} = require("chrome");
const MAX_ORDINAL = 99;
let Promise = require("sdk/core/promise");
let EventEmitter = require("devtools/shared/event-emitter");
let Telemetry = require("devtools/shared/telemetry");
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import("resource://gre/modules/Services.jsm");
@ -58,6 +59,7 @@ XPCOMUtils.defineLazyGetter(this, "Requisition", function() {
function Toolbox(target, selectedTool, hostType) {
this._target = target;
this._toolPanels = new Map();
this._telemetry = new Telemetry();
this._toolRegistered = this._toolRegistered.bind(this);
this._toolUnregistered = this._toolUnregistered.bind(this);
@ -191,8 +193,8 @@ Toolbox.prototype = {
open: function TBOX_open() {
let deferred = Promise.defer();
this._host.create().then(function(iframe) {
let domReady = function() {
this._host.create().then(iframe => {
let domReady = () => {
iframe.removeEventListener("DOMContentLoaded", domReady, true);
this.isReady = true;
@ -206,15 +208,17 @@ Toolbox.prototype = {
this._buildButtons();
this._addKeysToWindow();
this._telemetry.toolOpened("toolbox");
this.selectTool(this._defaultToolId).then(function(panel) {
this.emit("ready");
deferred.resolve();
}.bind(this));
}.bind(this);
};
iframe.addEventListener("DOMContentLoaded", domReady, true);
iframe.setAttribute("src", this._URL);
}.bind(this));
});
return deferred.promise;
},
@ -327,9 +331,7 @@ Toolbox.prototype = {
let buttons = CommandUtils.createButtons(toolbarSpec, this._target, this.doc, requisition);
let container = this.doc.getElementById("toolbox-buttons");
buttons.forEach(function(button) {
container.appendChild(button);
}.bind(this));
buttons.forEach(container.appendChild.bind(container));
},
/**
@ -423,6 +425,8 @@ Toolbox.prototype = {
let tab = this.doc.getElementById("toolbox-tab-" + id);
tab.setAttribute("selected", "true");
let prevToolId = this._currentToolId;
if (this._currentToolId == id) {
// Return the existing panel in order to have a consistent return value.
return Promise.resolve(this._toolPanels.get(id));
@ -433,7 +437,12 @@ Toolbox.prototype = {
}
let tab = this.doc.getElementById("toolbox-tab-" + id);
if (!tab) {
if (tab) {
if (prevToolId) {
this._telemetry.toolClosed(prevToolId);
}
this._telemetry.toolOpened(id);
} else {
throw new Error("No tool found");
}
@ -712,6 +721,8 @@ Toolbox.prototype = {
outstanding.push(this._host.destroy());
this._telemetry.destroy();
// Targets need to be notified that the toolbox is being torn down, so that
// remote protocol connections can be gracefully terminated.
if (this._target) {

View File

@ -13,6 +13,9 @@ Cu.import("resource:///modules/devtools/gDevTools.jsm");
Cu.import("resource:///modules/devtools/FloatingScrollbars.jsm");
Cu.import("resource:///modules/devtools/shared/event-emitter.js");
var require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
let Telemetry = require("devtools/shared/telemetry");
this.EXPORTED_SYMBOLS = ["ResponsiveUIManager"];
const MIN_WIDTH = 50;
@ -108,6 +111,7 @@ function ResponsiveUI(aWindow, aTab)
this.chromeDoc = aWindow.document;
this.container = aWindow.gBrowser.getBrowserContainer(this.browser);
this.stack = this.container.querySelector(".browserStack");
this._telemetry = new Telemetry();
// Try to load presets from prefs
if (Services.prefs.prefHasUserValue("devtools.responsiveUI.presets")) {
@ -178,6 +182,8 @@ function ResponsiveUI(aWindow, aTab)
this.tab.__responsiveUI = this;
this._telemetry.toolOpened("responsive");
ResponsiveUIManager.emit("on", this.tab, this);
}
@ -236,6 +242,7 @@ ResponsiveUI.prototype = {
this.stack.removeAttribute("responsivemode");
delete this.tab.__responsiveUI;
this._telemetry.toolClosed("responsive");
ResponsiveUIManager.emit("off", this.tab, this);
},

View File

@ -35,6 +35,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "VariablesView",
XPCOMUtils.defineLazyModuleGetter(this, "devtools",
"resource://gre/modules/devtools/Loader.jsm");
let Telemetry = devtools.require("devtools/shared/telemetry");
const SCRATCHPAD_CONTEXT_CONTENT = 1;
const SCRATCHPAD_CONTEXT_BROWSER = 2;
const SCRATCHPAD_L10N = "chrome://browser/locale/devtools/scratchpad.properties";
@ -46,6 +48,10 @@ const BUTTON_POSITION_DONT_SAVE = 2;
const BUTTON_POSITION_REVERT = 0;
const VARIABLES_VIEW_URL = "chrome://browser/content/devtools/widgets/VariablesView.xul";
// Because we have no constructor / destructor where we can log metrics we need
// to do so here.
let telemetry = new Telemetry();
telemetry.toolOpened("scratchpad");
/**
* The scratchpad object handles the Scratchpad window functionality.
@ -1390,6 +1396,7 @@ var Scratchpad = {
}
if (shouldClose) {
telemetry.toolClosed("scratchpad");
window.close();
}
if (aCallback) {

View File

@ -8,12 +8,13 @@ this.EXPORTED_SYMBOLS = [ "DeveloperToolbar", "CommandUtils" ];
const NS_XHTML = "http://www.w3.org/1999/xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource:///modules/devtools/Commands.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/devtools/Commands.jsm");
const Node = Components.interfaces.nsIDOMNode;
const Node = Ci.nsIDOMNode;
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
@ -40,16 +41,18 @@ XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
"resource:///modules/devtools/shared/event-emitter.js");
XPCOMUtils.defineLazyGetter(this, "prefBranch", function() {
let prefService = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
let prefService = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefService);
return prefService.getBranch(null)
.QueryInterface(Components.interfaces.nsIPrefBranch2);
.QueryInterface(Ci.nsIPrefBranch2);
});
XPCOMUtils.defineLazyGetter(this, "toolboxStrings", function () {
return Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties");
});
let Telemetry = devtools.require("devtools/shared/telemetry");
const converters = require("gcli/converters");
/**
@ -61,8 +64,7 @@ let CommandUtils = {
* @param aPref The name of the preference to read
*/
getCommandbarSpec: function CU_getCommandbarSpec(aPref) {
let value = prefBranch.getComplexValue(aPref,
Components.interfaces.nsISupportsString).data;
let value = prefBranch.getComplexValue(aPref, Ci.nsISupportsString).data;
return JSON.parse(value);
},
@ -188,8 +190,7 @@ XPCOMUtils.defineLazyGetter(this, "isLinux", function () {
});
XPCOMUtils.defineLazyGetter(this, "OS", function () {
let os = Components.classes["@mozilla.org/xre/app-info;1"]
.getService(Components.interfaces.nsIXULRuntime).OS;
let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
return os;
});
@ -207,6 +208,7 @@ this.DeveloperToolbar = function DeveloperToolbar(aChromeWindow, aToolbarElement
this._element.hidden = true;
this._doc = this._element.ownerDocument;
this._telemetry = new Telemetry();
this._lastState = NOTIFICATIONS.HIDE;
this._pendingShowCallback = undefined;
this._pendingHide = false;
@ -339,6 +341,8 @@ DeveloperToolbar.prototype.show = function DT_show(aFocus, aCallback)
Services.prefs.setBoolPref("devtools.toolbar.visible", true);
this._telemetry.toolOpened("developertoolbar");
this._notify(NOTIFICATIONS.LOAD);
this._pendingShowCallback = aCallback;
this._pendingHide = false;
@ -500,6 +504,7 @@ DeveloperToolbar.prototype.hide = function DT_hide()
this._doc.getElementById("Tools:DevToolbar").setAttribute("checked", "false");
this.destroy();
this._telemetry.toolClosed("developertoolbar");
this._notify(NOTIFICATIONS.HIDE);
};

View File

@ -0,0 +1,259 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Telemetry.
*
* To add metrics for a tool:
*
* 1. Create boolean, flag and exponential entries in
* toolkit/components/telemetry/Histograms.json. Each type is optional but it
* is best if all three can be included.
*
* 2. Add your chart entries to browser/devtools/shared/telemetry.js
* (Telemetry.prototype._histograms):
* mytoolname: {
* histogram: "DEVTOOLS_MYTOOLNAME_OPENED_BOOLEAN",
* userHistogram: "DEVTOOLS_MYTOOLNAME_OPENED_PER_USER_FLAG",
* timerHistogram: "DEVTOOLS_MYTOOLNAME_TIME_ACTIVE_SECONDS"
* },
*
* 3. Include this module at the top of your tool. Use:
* let Telemetry = require("devtools/shared/telemetry")
*
* 4. Create a telemetry instance in your tool's constructor:
* this._telemetry = new Telemetry();
*
* 5. When your tool is opened call:
* this._telemetry.toolOpened("mytoolname");
*
* 6. When your tool is closed call:
* this._telemetry.toolClosed("mytoolname");
*
* Note:
* You can view telemetry stats for your local Firefox instance via
* about:telemetry.
*
* You can view telemetry stats for large groups of Firefox users at
* metrics.mozilla.com.
*/
const TOOLS_OPENED_PREF = "devtools.telemetry.tools.opened.version";
this.Telemetry = function() {
// Bind pretty much all functions so that callers do not need to.
this.toolOpened = this.toolOpened.bind(this);
this.toolClosed = this.toolClosed.bind(this);
this.log = this.log.bind(this);
this.logOncePerBrowserVersion = this.logOncePerBrowserVersion.bind(this);
this.destroy = this.destroy.bind(this);
this._timers = new Map();
};
module.exports = Telemetry;
let {Cc, Ci, Cu} = require("chrome");
let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
Telemetry.prototype = {
_histograms: {
toolbox: {
timerHistogram: "DEVTOOLS_TOOLBOX_TIME_ACTIVE_SECONDS"
},
options: {
histogram: "DEVTOOLS_OPTIONS_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_OPTIONS_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_OPTIONS_TIME_ACTIVE_SECONDS"
},
webconsole: {
histogram: "DEVTOOLS_WEBCONSOLE_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_WEBCONSOLE_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_WEBCONSOLE_TIME_ACTIVE_SECONDS"
},
browserconsole: {
histogram: "DEVTOOLS_BROWSERCONSOLE_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_BROWSERCONSOLE_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_BROWSERCONSOLE_TIME_ACTIVE_SECONDS"
},
inspector: {
histogram: "DEVTOOLS_INSPECTOR_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_INSPECTOR_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_INSPECTOR_TIME_ACTIVE_SECONDS"
},
ruleview: {
histogram: "DEVTOOLS_RULEVIEW_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_RULEVIEW_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_RULEVIEW_TIME_ACTIVE_SECONDS"
},
computedview: {
histogram: "DEVTOOLS_COMPUTEDVIEW_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_COMPUTEDVIEW_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_COMPUTEDVIEW_TIME_ACTIVE_SECONDS"
},
layoutview: {
histogram: "DEVTOOLS_LAYOUTVIEW_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_LAYOUTVIEW_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_LAYOUTVIEW_TIME_ACTIVE_SECONDS"
},
fontinspector: {
histogram: "DEVTOOLS_FONTINSPECTOR_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_FONTINSPECTOR_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_FONTINSPECTOR_TIME_ACTIVE_SECONDS"
},
jsdebugger: {
histogram: "DEVTOOLS_JSDEBUGGER_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_JSDEBUGGER_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_JSDEBUGGER_TIME_ACTIVE_SECONDS"
},
jsbrowserdebugger: {
histogram: "DEVTOOLS_JSBROWSERDEBUGGER_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_JSBROWSERDEBUGGER_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_JSBROWSERDEBUGGER_TIME_ACTIVE_SECONDS"
},
styleeditor: {
histogram: "DEVTOOLS_STYLEEDITOR_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_STYLEEDITOR_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_STYLEEDITOR_TIME_ACTIVE_SECONDS"
},
jsprofiler: {
histogram: "DEVTOOLS_JSPROFILER_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_JSPROFILER_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_JSPROFILER_TIME_ACTIVE_SECONDS"
},
netmonitor: {
histogram: "DEVTOOLS_NETMONITOR_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_NETMONITOR_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_NETMONITOR_TIME_ACTIVE_SECONDS"
},
tilt: {
histogram: "DEVTOOLS_TILT_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_TILT_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_TILT_TIME_ACTIVE_SECONDS"
},
paintflashing: {
histogram: "DEVTOOLS_PAINTFLASHING_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_PAINTFLASHING_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_PAINTFLASHING_TIME_ACTIVE_SECONDS"
},
scratchpad: {
histogram: "DEVTOOLS_SCRATCHPAD_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_SCRATCHPAD_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_SCRATCHPAD_TIME_ACTIVE_SECONDS"
},
responsive: {
histogram: "DEVTOOLS_RESPONSIVE_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_RESPONSIVE_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_RESPONSIVE_TIME_ACTIVE_SECONDS"
},
developertoolbar: {
histogram: "DEVTOOLS_DEVELOPERTOOLBAR_OPENED_BOOLEAN",
userHistogram: "DEVTOOLS_DEVELOPERTOOLBAR_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_DEVELOPERTOOLBAR_TIME_ACTIVE_SECONDS"
}
},
/**
* Add an entry to a histogram.
*
* @param {String} id
* Used to look up the relevant histogram ID and log true to that
* histogram.
*/
toolOpened: function(id) {
let charts = this._histograms[id];
if (!charts) {
dump('Warning: An attempt was made to open a tool with an id of "' + id +
'", which is not listed in Telemetry._histograms. ' +
"Location: telemetry.js/toolOpened()\n");
return;
}
if (charts.histogram) {
this.log(charts.histogram, true);
}
if (charts.userHistogram) {
this.logOncePerBrowserVersion(charts.userHistogram, true);
}
if (charts.timerHistogram) {
this._timers.set(charts.timerHistogram, new Date());
}
},
toolClosed: function(id) {
let charts = this._histograms[id];
if (!charts || !charts.timerHistogram) {
return;
}
let startTime = this._timers.get(charts.timerHistogram);
if (startTime) {
let time = (new Date() - startTime) / 1000;
this.log(charts.timerHistogram, time);
this._timers.delete(charts.timerHistogram);
}
},
/**
* Log a value to a histogram.
*
* @param {String} histogramId
* Histogram in which the data is to be stored.
* @param value
* Value to store.
*/
log: function(histogramId, value) {
if (histogramId) {
let histogram;
try {
let histogram = Services.telemetry.getHistogramById(histogramId);
histogram.add(value);
} catch(e) {
dump("Warning: An attempt was made to write to the " + histogramId +
" histogram, which is not defined in Histograms.json\n");
}
}
},
/**
* Log info about usage once per browser version. This allows us to discover
* how many individual users are using our tools for each browser version.
*
* @param {String} perUserHistogram
* Histogram in which the data is to be stored.
*/
logOncePerBrowserVersion: function(perUserHistogram, value) {
let currentVersion = appInfo.version;
let latest = Services.prefs.getCharPref(TOOLS_OPENED_PREF);
let latestObj = JSON.parse(latest);
let lastVersionHistogramUpdated = latestObj[perUserHistogram];
if (typeof lastVersionHistogramUpdated == "undefined" ||
lastVersionHistogramUpdated !== currentVersion) {
latestObj[perUserHistogram] = currentVersion;
latest = JSON.stringify(latestObj);
Services.prefs.setCharPref(TOOLS_OPENED_PREF, latest);
this.log(perUserHistogram, value);
}
},
destroy: function() {
for (let [histogram, time] of this._timers) {
time = (new Date() - time) / 1000;
this.log(histogram, time);
this._timers.delete(histogram);
}
}
};
XPCOMUtils.defineLazyGetter(this, "appInfo", function() {
return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
});

View File

@ -12,6 +12,8 @@ relativesrcdir = @relativesrcdir@
include $(DEPTH)/config/autoconf.mk
MOCHITEST_BROWSER_FILES = \
browser_telemetry_toolboxtabs.js \
browser_telemetry_buttonsandsidebar.js \
browser_require_basic.js \
browser_templater_basic.js \
browser_toolbar_basic.js \

View File

@ -0,0 +1,179 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_buttonsandsidebar.js</p>";
// Because we need to gather stats for the period of time that a tool has been
// opened we make use of setTimeout() to create tool active times.
const TOOL_DELAY = 200;
let {Promise} = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
let Telemetry = require("devtools/shared/telemetry");
function init() {
Telemetry.prototype.telemetryInfo = {};
Telemetry.prototype._oldlog = Telemetry.prototype.log;
Telemetry.prototype.log = function(histogramId, value) {
if (histogramId) {
if (!this.telemetryInfo[histogramId]) {
this.telemetryInfo[histogramId] = [];
}
this.telemetryInfo[histogramId].push(value);
}
}
testButtons();
}
function testButtons() {
info("Testing buttons");
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
let container = toolbox.doc.getElementById("toolbox-buttons");
let buttons = container.getElementsByTagName("toolbarbutton");
// Copy HTMLCollection to array.
buttons = Array.prototype.slice.call(buttons);
(function testButton() {
let button = buttons.pop();
if (button) {
info("Clicking button " + button.id);
button.click();
delayedClicks(button, 3).then(function(button) {
if (buttons.length == 0) {
// Remove scratchpads
let wins = Services.wm.getEnumerator("devtools:scratchpad");
while (wins.hasMoreElements()) {
let win = wins.getNext();
info("Closing scratchpad window");
win.close();
}
testSidebar();
} else {
setTimeout(testButton, TOOL_DELAY);
}
});
}
})();
}).then(null, reportError);
}
function delayedClicks(node, clicks) {
let deferred = Promise.defer();
let clicked = 0;
setTimeout(function delayedClick() {
info("Clicking button " + node.id);
node.click();
clicked++;
if (clicked >= clicks) {
deferred.resolve(node);
} else {
setTimeout(delayedClick, TOOL_DELAY);
}
}, TOOL_DELAY);
return deferred.promise;
}
function testSidebar() {
info("Testing sidebar");
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
let inspector = toolbox.getCurrentPanel();
let sidebarTools = ["ruleview", "computedview", "fontinspector", "layoutview"];
// Concatenate the array with itself so that we can open each tool twice.
sidebarTools.push.apply(sidebarTools, sidebarTools);
setTimeout(function selectSidebarTab() {
let tool = sidebarTools.pop();
if (tool) {
inspector.sidebar.select(tool);
setTimeout(function() {
setTimeout(selectSidebarTab, TOOL_DELAY);
}, TOOL_DELAY);
} else {
checkResults();
}
}, TOOL_DELAY);
});
}
function checkResults() {
let result = Telemetry.prototype.telemetryInfo;
for (let [histId, value] of Iterator(result)) {
if (histId.startsWith("DEVTOOLS_INSPECTOR_")) {
// Inspector stats are tested in browser_telemetry_toolboxtabs.js so we
// skip them here because we only open the inspector once for this test.
continue;
}
if (histId.endsWith("OPENED_PER_USER_FLAG")) {
ok(value.length === 1 && value[0] === true,
"Per user value " + histId + " has a single value of true");
} else if (histId.endsWith("OPENED_BOOLEAN")) {
ok(value.length > 1, histId + " has more than one entry");
let okay = value.every(function(element) {
return element === true;
});
ok(okay, "All " + histId + " entries are === true");
} else if (histId.endsWith("TIME_ACTIVE_SECONDS")) {
ok(value.length > 1, histId + " has more than one entry");
let okay = value.every(function(element) {
return element > 0;
});
ok(okay, "All " + histId + " entries have time > 0");
}
}
finishUp();
}
function reportError(error) {
let stack = " " + error.stack.replace(/\n?.*?@/g, "\n JS frame :: ");
ok(false, "ERROR: " + error + " at " + error.fileName + ":" +
error.lineNumber + "\n\nStack trace:" + stack);
finishUp();
}
function finishUp() {
gBrowser.removeCurrentTab();
Telemetry.prototype.log = Telemetry.prototype._oldlog;
delete Telemetry.prototype._oldlog;
delete Telemetry.prototype.telemetryInfo;
TargetFactory = Services = Promise = require = null;
finish();
}
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
waitForFocus(init, content);
}, true);
content.location = TEST_URI;
}

View File

@ -0,0 +1,125 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_toolboxtabs.js</p>";
// Because we need to gather stats for the period of time that a tool has been
// opened we make use of setTimeout() to create tool active times.
const TOOL_DELAY = 200;
let {Promise} = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
let Telemetry = require("devtools/shared/telemetry");
let tabsToTest = ["inspector", "options", "webconsole", "jsdebugger",
"styleeditor", "jsprofiler", "netmonitor"];
function init() {
Telemetry.prototype.telemetryInfo = {};
Telemetry.prototype._oldlog = Telemetry.prototype.log;
Telemetry.prototype.log = function(histogramId, value) {
if (histogramId) {
if (!this.telemetryInfo[histogramId]) {
this.telemetryInfo[histogramId] = [];
}
this.telemetryInfo[histogramId].push(value);
}
}
testNextTool();
}
function testNextTool() {
let nextTool = tabsToTest.pop();
if (nextTool) {
openToolboxTabTwice(nextTool);
}
}
function openToolboxTabTwice(id, secondPass) {
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, id).then(function(toolbox) {
info("Toolbox tab " + id + " opened");
toolbox.once("destroyed", function() {
if (secondPass) {
if (tabsToTest.length > 0) {
testNextTool();
} else {
checkResults();
}
} else {
openToolboxTabTwice(id, true);
}
});
// We use a timeout to check the tools active time
setTimeout(function() {
gDevTools.closeToolbox(target);
}, TOOL_DELAY);
}).then(null, reportError);
}
function checkResults() {
let result = Telemetry.prototype.telemetryInfo;
for (let [histId, value] of Iterator(result)) {
if (histId.endsWith("OPENED_PER_USER_FLAG")) {
ok(value.length === 1 && value[0] === true,
"Per user value " + histId + " has a single value of true");
} else if (histId.endsWith("OPENED_BOOLEAN")) {
ok(value.length > 1, histId + " has more than one entry");
let okay = value.every(function(element) {
return element === true;
});
ok(okay, "All " + histId + " entries are === true");
} else if (histId.endsWith("TIME_ACTIVE_SECONDS")) {
ok(value.length > 1, histId + " has more than one entry");
let okay = value.every(function(element) {
return element > 0;
});
ok(okay, "All " + histId + " entries have time > 0");
}
}
finishUp();
}
function reportError(error) {
let stack = " " + error.stack.replace(/\n?.*?@/g, "\n JS frame :: ");
ok(false, "ERROR: " + error + " at " + error.fileName + ":" +
error.lineNumber + "\n\nStack trace:" + stack);
finishUp();
}
function finishUp() {
gBrowser.removeCurrentTab();
Telemetry.prototype.log = Telemetry.prototype._oldlog;
delete Telemetry.prototype._oldlog;
delete Telemetry.prototype.telemetryInfo;
TargetFactory = tabsToTest = Services = Promise = require = null;
finish();
}
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
waitForFocus(init, content);
}, true);
content.location = TEST_URI;
}

View File

@ -11,6 +11,7 @@ let {TiltVisualizer} = require("devtools/tilt/tilt-visualizer");
let TiltGL = require("devtools/tilt/tilt-gl");
let TiltUtils = require("devtools/tilt/tilt-utils");
let EventEmitter = require("devtools/shared/event-emitter");
let Telemetry = require("devtools/shared/telemetry");
Cu.import("resource://gre/modules/Services.jsm");
@ -94,6 +95,8 @@ function Tilt(aWindow)
EventEmitter.decorate(this);
this.setup();
this._telemetry = new Telemetry();
}
Tilt.prototype = {
@ -115,7 +118,10 @@ Tilt.prototype = {
// if the visualizer for the current tab is already open, destroy it now
if (this.visualizers[id]) {
this.destroy(id, true);
this._telemetry.toolClosed("tilt");
return;
} else {
this._telemetry.toolOpened("tilt");
}
// create a visualizer instance for the current tab

View File

@ -10,7 +10,6 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
@ -37,6 +36,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
XPCOMUtils.defineLazyModuleGetter(this, "ViewHelpers",
"resource:///modules/devtools/ViewHelpers.jsm");
let Telemetry = devtools.require("devtools/shared/telemetry");
const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
let l10n = new WebConsoleUtils.l10n(STRINGS_URI);
@ -515,6 +516,7 @@ WebConsole.prototype = {
function BrowserConsole()
{
WebConsole.apply(this, arguments);
this._telemetry = new Telemetry();
}
ViewHelpers.create({ constructor: BrowserConsole, proto: WebConsole.prototype },
@ -545,6 +547,7 @@ ViewHelpers.create({ constructor: BrowserConsole, proto: WebConsole.prototype },
window.addEventListener("unload", onClose);
this._bc_init = this.$init().then((aReason) => {
this._telemetry.toolOpened("browserconsole");
let title = this.ui.rootElement.getAttribute("browserConsoleTitle");
this.ui.rootElement.setAttribute("title", title);
@ -571,6 +574,8 @@ ViewHelpers.create({ constructor: BrowserConsole, proto: WebConsole.prototype },
return this._bc_destroyer.promise;
}
this._telemetry.toolClosed("browserconsole");
this._bc_destroyer = Promise.defer();
let chromeWindow = this.chromeWindow;

View File

@ -3311,5 +3311,263 @@
"high": "10000",
"n_buckets": "1000",
"description": "The time (in milliseconds) that it took a 'detach' request to go round trip."
},
"DEVTOOLS_OPTIONS_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Options panel been opened?"
},
"DEVTOOLS_WEBCONSOLE_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Web Console been opened?"
},
"DEVTOOLS_BROWSERCONSOLE_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Browser Console been opened?"
},
"DEVTOOLS_INSPECTOR_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Inspector been opened?"
},
"DEVTOOLS_RULEVIEW_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Rule View been opened?"
},
"DEVTOOLS_COMPUTEDVIEW_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Computed View been opened?"
},
"DEVTOOLS_LAYOUTVIEW_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Layout View been opened?"
},
"DEVTOOLS_FONTINSPECTOR_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Font Inspector been opened?"
},
"DEVTOOLS_JSDEBUGGER_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Debugger been opened?"
},
"DEVTOOLS_JSBROWSERDEBUGGER_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Browser Debugger been opened?"
},
"DEVTOOLS_STYLEEDITOR_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Style Editor been opened?"
},
"DEVTOOLS_JSPROFILER_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's JS Profiler been opened?"
},
"DEVTOOLS_NETMONITOR_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Network Monitor been opened?"
},
"DEVTOOLS_PAINTFLASHING_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Paint Flashing been opened via the toolbox button?"
},
"DEVTOOLS_TILT_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Tilt been opened via the toolbox button?"
},
"DEVTOOLS_SCRATCHPAD_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Scratchpad been opened via the toolbox button?"
},
"DEVTOOLS_RESPONSIVE_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Responsive View been opened via the toolbox button?"
},
"DEVTOOLS_DEVELOPERTOOLBAR_OPENED_BOOLEAN": {
"kind": "boolean",
"description": "How many times has the devtool's Developer Toolbar been opened via the toolbox button?"
},
"DEVTOOLS_OPTIONS_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many times has the devtool's Options panel been opened?"
},
"DEVTOOLS_WEBCONSOLE_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Web Console?"
},
"DEVTOOLS_BROWSERCONSOLE_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Browser Console?"
},
"DEVTOOLS_INSPECTOR_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Inspector?"
},
"DEVTOOLS_RULEVIEW_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Rule View?"
},
"DEVTOOLS_COMPUTEDVIEW_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Computed View?"
},
"DEVTOOLS_LAYOUTVIEW_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Layout View?"
},
"DEVTOOLS_FONTINSPECTOR_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Font Inspector?"
},
"DEVTOOLS_JSDEBUGGER_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Debugger?"
},
"DEVTOOLS_JSBROWSERDEBUGGER_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Browser Debugger?"
},
"DEVTOOLS_STYLEEDITOR_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Style Editor?"
},
"DEVTOOLS_JSPROFILER_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's JS Profiler?"
},
"DEVTOOLS_NETMONITOR_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Network Monitor?"
},
"DEVTOOLS_PAINTFLASHING_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Paint Flashing been opened via the toolbox button?"
},
"DEVTOOLS_TILT_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Tilt been opened via the toolbox button?"
},
"DEVTOOLS_SCRATCHPAD_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Scratchpad been opened via the toolbox button?"
},
"DEVTOOLS_RESPONSIVE_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Responsive View been opened via the toolbox button?"
},
"DEVTOOLS_DEVELOPERTOOLBAR_OPENED_PER_USER_FLAG": {
"kind": "flag",
"description": "How many users have opened the devtool's Developer Toolbar been opened via the toolbox button?"
},
"DEVTOOLS_TOOLBOX_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the toolbox been active (seconds)"
},
"DEVTOOLS_OPTIONS_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the options panel been active (seconds)"
},
"DEVTOOLS_WEBCONSOLE_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the web console been active (seconds)"
},
"DEVTOOLS_BROWSERCONSOLE_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the browser console been active (seconds)"
},
"DEVTOOLS_INSPECTOR_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the inspector been active (seconds)"
},
"DEVTOOLS_RULEVIEW_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the rule view been active (seconds)"
},
"DEVTOOLS_COMPUTEDVIEW_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the computed view been active (seconds)"
},
"DEVTOOLS_LAYOUTVIEW_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the layout view been active (seconds)"
},
"DEVTOOLS_FONTINSPECTOR_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the font inspector been active (seconds)"
},
"DEVTOOLS_JSDEBUGGER_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the JS debugger been active (seconds)"
},
"DEVTOOLS_JSBROWSERDEBUGGER_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the JS browser debugger been active (seconds)"
},
"DEVTOOLS_STYLEEDITOR_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the style editor been active (seconds)"
},
"DEVTOOLS_JSPROFILER_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the JS profiler been active (seconds)"
},
"DEVTOOLS_NETMONITOR_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the network monitor been active (seconds)"
},
"DEVTOOLS_PAINTFLASHING_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has paint flashing been active (seconds)"
},
"DEVTOOLS_TILT_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has Tilt been active (seconds)"
},
"DEVTOOLS_SCRATCHPAD_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has Scratchpad been active (seconds)"
},
"DEVTOOLS_RESPONSIVE_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the responsive view been active (seconds)"
},
"DEVTOOLS_DEVELOPERTOOLBAR_TIME_ACTIVE_SECONDS": {
"kind": "exponential",
"high": "10000000",
"n_buckets": 100,
"description": "How long has the developer toolbar been active (seconds)"
}
}