Bug 1447302 - add telemetry probes for accessibility inspector Developer Tool panel. r=jdescottes
authorYura Zenevich <yura.zenevich@gmail.com>
Mon, 09 Apr 2018 15:26:47 -0400
changeset 467530 529b9c25a0c2497dd6721f0794481eeb06be5173
parent 467529 bb4fe8662ae37b2dd680f2d7383d81e1d3008c5f
child 467531 ba5089a967b993f911896e1f787fb9c7c9b406c3
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdescottes
bugs1447302
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1447302 - add telemetry probes for accessibility inspector Developer Tool panel. r=jdescottes MozReview-Commit-ID: 6tcGHq86goa
devtools/client/accessibility/accessibility-panel.js
devtools/client/accessibility/components/Accessible.js
devtools/client/accessibility/components/Description.js
devtools/client/accessibility/components/Toolbar.js
devtools/client/accessibility/constants.js
devtools/client/accessibility/picker.js
devtools/client/framework/devtools.js
devtools/client/framework/toolbox-options.js
devtools/client/inspector/inspector.js
devtools/client/shared/telemetry.js
toolkit/components/telemetry/Histograms.json
toolkit/components/telemetry/Scalars.yaml
--- a/devtools/client/accessibility/accessibility-panel.js
+++ b/devtools/client/accessibility/accessibility-panel.js
@@ -1,17 +1,20 @@
 /* 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/. */
 "use strict";
 
 const { AccessibilityFront } = require("devtools/shared/fronts/accessibility");
 const EventEmitter = require("devtools/shared/event-emitter");
 
+const Telemetry = require("devtools/client/shared/telemetry");
+
 const { Picker } = require("./picker");
+const { A11Y_SERVICE_DURATION } = require("./constants");
 
 // The panel's window global is an EventEmitter firing the following events:
 const EVENTS = {
   // When the accessibility inspector has a new accessible front selected.
   NEW_ACCESSIBLE_FRONT_SELECTED: "Accessibility:NewAccessibleFrontSelected",
   // When the accessibility inspector has a new accessible front highlighted.
   NEW_ACCESSIBLE_FRONT_HIGHLIGHTED: "Accessibility:NewAccessibleFrontHighlighted",
   // When the accessibility inspector has a new accessible front inspected.
@@ -30,16 +33,17 @@ function AccessibilityPanel(iframeWindow
   this._toolbox = toolbox;
 
   this.onTabNavigated = this.onTabNavigated.bind(this);
   this.onPanelVisibilityChange = this.onPanelVisibilityChange.bind(this);
   this.onNewAccessibleFrontSelected =
     this.onNewAccessibleFrontSelected.bind(this);
   this.onAccessibilityInspectorUpdated =
     this.onAccessibilityInspectorUpdated.bind(this);
+  this.updateA11YServiceDurationTimer = this.updateA11YServiceDurationTimer.bind(this);
   this.updatePickerButton = this.updatePickerButton.bind(this);
 
   EventEmitter.decorate(this);
 }
 
 AccessibilityPanel.prototype = {
   /**
    * Open is effectively an asynchronous constructor.
@@ -55,16 +59,19 @@ AccessibilityPanel.prototype = {
       resolver = resolve;
     });
 
     // Local monitoring needs to make the target remote.
     if (!this.target.isRemote) {
       await this.target.makeRemote();
     }
 
+    this._telemetry = new Telemetry();
+    this.panelWin.gTelemetry = this._telemetry;
+
     this.target.on("navigate", this.onTabNavigated);
     this._toolbox.on("select", this.onPanelVisibilityChange);
 
     this.panelWin.EVENTS = EVENTS;
     EventEmitter.decorate(this.panelWin);
     this.panelWin.on(EVENTS.NEW_ACCESSIBLE_FRONT_SELECTED,
       this.onNewAccessibleFrontSelected);
     this.panelWin.on(EVENTS.ACCESSIBILITY_INSPECTOR_UPDATED,
@@ -79,16 +86,20 @@ AccessibilityPanel.prototype = {
     this._walker = await this._front.getWalker();
 
     this._isOldVersion = !(await this.target.actorHasMethod("accessibility", "enable"));
     if (!this._isOldVersion) {
       await this._front.bootstrap();
       this.picker = new Picker(this);
     }
 
+    this.updateA11YServiceDurationTimer();
+    this._front.on("init", this.updateA11YServiceDurationTimer);
+    this._front.on("shutdown", this.updateA11YServiceDurationTimer);
+
     this.isReady = true;
     this.emit("ready");
     resolver(this);
     return this._opening;
   },
 
   onNewAccessibleFrontSelected(selected) {
     this.emit("new-accessible-front-selected", selected);
@@ -132,21 +143,34 @@ AccessibilityPanel.prototype = {
     if (!this.shouldRefresh) {
       return;
     }
     // Alright reset the flag we are about to refresh the panel.
     this.shouldRefresh = false;
     this.postContentMessage("initialize", this._front, this._walker, this._isOldVersion);
   },
 
+  updateA11YServiceDurationTimer() {
+    if (this._front.enabled) {
+      this._telemetry.startTimer(A11Y_SERVICE_DURATION);
+    } else {
+      this._telemetry.stopTimer(A11Y_SERVICE_DURATION);
+    }
+  },
+
   selectAccessible(accessibleFront) {
     this.postContentMessage("selectAccessible", this._walker, accessibleFront);
   },
 
-  selectAccessibleForNode(nodeFront) {
+  selectAccessibleForNode(nodeFront, reason) {
+    if (reason) {
+      this._telemetry.logKeyedScalar(
+        "devtools.accessibility.select_accessible_for_node", reason, 1);
+    }
+
     this.postContentMessage("selectNodeAccessible", this._walker, nodeFront);
   },
 
   highlightAccessible(accessibleFront) {
     this.postContentMessage("highlightAccessible", this._walker, accessibleFront);
   },
 
   postContentMessage(type, ...args) {
@@ -196,33 +220,39 @@ AccessibilityPanel.prototype = {
       return;
     }
 
     let resolver;
     this._destroying = new Promise(resolve => {
       resolver = resolve;
     });
 
+    this._telemetry.destroy();
+
     this.target.off("navigate", this.onTabNavigated);
     this._toolbox.off("select", this.onPanelVisibilityChange);
 
     this.panelWin.off(EVENTS.NEW_ACCESSIBLE_FRONT_SELECTED,
       this.onNewAccessibleFrontSelected);
     this.panelWin.off(EVENTS.ACCESSIBILITY_INSPECTOR_UPDATED,
       this.onAccessibilityInspectorUpdated);
 
     this.picker.release();
     this.picker = null;
 
     if (this._front) {
+      this._front.off("init", this.updateA11YServiceDurationTimer);
+      this._front.off("shutdown", this.updateA11YServiceDurationTimer);
       await this._front.destroy();
     }
 
     this._front = null;
+    this._telemetry = null;
     this.panelWin.gToolbox = null;
+    this.panelWin.gTelemetry = null;
 
     this.emit("destroyed");
 
     resolver();
   }
 };
 
 // Exports from this module
--- a/devtools/client/accessibility/components/Accessible.js
+++ b/devtools/client/accessibility/components/Accessible.js
@@ -1,14 +1,14 @@
 /* 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/. */
 "use strict";
 
-/* global EVENTS, gToolbox */
+/* global EVENTS, gTelemetry, gToolbox */
 
 // React & Redux
 const { createFactory, Component } = require("devtools/client/shared/vendor/react");
 const { div, span } = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
@@ -155,16 +155,20 @@ class Accessible extends Component {
     if (!gToolbox) {
       return;
     }
 
     gToolbox.highlighterUtils.unhighlight();
   }
 
   selectNode(nodeFront, reason = "accessibility") {
+    if (gTelemetry) {
+      gTelemetry.actionOccurred("accessibilityNodeInspected");
+    }
+
     if (!gToolbox) {
       return;
     }
 
     gToolbox.selectTool("inspector").then(() =>
       gToolbox.selection.setNodeFront(nodeFront, reason));
   }
 
--- a/devtools/client/accessibility/components/Description.js
+++ b/devtools/client/accessibility/components/Description.js
@@ -1,25 +1,29 @@
 /* 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/. */
 "use strict";
 
+/* global gTelemetry */
+
 // React & Redux
 const { createFactory, Component } = require("devtools/client/shared/vendor/react");
 const { div, p, img } = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const Button = createFactory(require("./Button"));
 const { enable, updateCanBeEnabled } = require("../actions/ui");
 
 // Localization
 const { L10N } = require("../utils/l10n");
 
+const { A11Y_SERVICE_ENABLED_COUNT } = require("../constants");
+
 class OldVersionDescription extends Component {
   render() {
     return (
       div({ className: "description" },
         p({ className: "general" },
           img({
             src: "chrome://devtools/skin/images/accessibility.svg",
             alt: L10N.getStr("accessibility.logo")
@@ -60,16 +64,21 @@ class Description extends Component {
   componentWillUnmount() {
     this.props.accessibility.off("can-be-enabled-change",
       this.onCanBeEnabledChange);
   }
 
   onEnable() {
     let { accessibility, dispatch } = this.props;
     this.setState({ enabling: true });
+
+    if (gTelemetry) {
+      gTelemetry.logCountScalar(A11Y_SERVICE_ENABLED_COUNT, 1);
+    }
+
     dispatch(enable(accessibility))
       .then(() => this.setState({ enabling: false }))
       .catch(() => this.setState({ enabling: false }));
   }
 
   onCanBeEnabledChange(canBeEnabled) {
     this.props.dispatch(updateCanBeEnabled(canBeEnabled));
   }
--- a/devtools/client/accessibility/components/Toolbar.js
+++ b/devtools/client/accessibility/components/Toolbar.js
@@ -45,16 +45,17 @@ class Toolbar extends Component {
 
   onCanBeDisabledChange(canBeDisabled) {
     this.props.dispatch(updateCanBeDisabled(canBeDisabled));
   }
 
   onDisable() {
     let { accessibility, dispatch } = this.props;
     this.setState({ disabling: true });
+
     dispatch(disable(accessibility))
       .then(() => this.setState({ disabling: false }))
       .catch(() => this.setState({ disabling: false }));
   }
 
   render() {
     let { canBeDisabled } = this.props;
     let { disabling } = this.state;
--- a/devtools/client/accessibility/constants.js
+++ b/devtools/client/accessibility/constants.js
@@ -56,8 +56,12 @@ exports.ACCESSIBLE_EVENTS = [
   "name-change",
   "reorder",
   "shortcut-change",
   "states-change",
   "text-change",
   "value-change",
   "index-in-parent-change"
 ];
+
+// Telemetry name constants.
+exports.A11Y_SERVICE_DURATION = "DEVTOOLS_ACCESSIBILITY_SERVICE_TIME_ACTIVE_SECONDS";
+exports.A11Y_SERVICE_ENABLED_COUNT = "devtools.accessibility.service_enabled_count";
--- a/devtools/client/accessibility/picker.js
+++ b/devtools/client/accessibility/picker.js
@@ -25,16 +25,20 @@ class Picker {
   get walker() {
     return this._panel._walker;
   }
 
   get pickerButton() {
     return this.toolbox.pickerButton;
   }
 
+  get _telemetry() {
+    return this._panel._telemetry;
+  }
+
   release() {
     this._panel = null;
   }
 
   emit(event, data) {
     this.toolbox.emit(event, data);
   }
 
@@ -131,16 +135,19 @@ class Picker {
       return;
     }
 
     this.isPicking = false;
 
     this.pickerButton.isChecked = false;
 
     await this.walker.cancelPick();
+
+    this._telemetry.toolClosed("accessibilityPickerUsed");
+
     this.walker.off("picker-accessible-hovered", this.onPickerAccessibleHovered);
     this.walker.off("picker-accessible-picked", this.onPickerAccessiblePicked);
     this.walker.off("picker-accessible-previewed", this.onPickerAccessiblePreviewed);
     this.walker.off("picker-accessible-canceled", this.onPickerAccessibleCanceled);
 
     this.emit("picker-stopped");
   }
 
@@ -159,16 +166,17 @@ class Picker {
     this.pickerButton.isChecked = true;
 
     this.walker.on("picker-accessible-hovered", this.onPickerAccessibleHovered);
     this.walker.on("picker-accessible-picked", this.onPickerAccessiblePicked);
     this.walker.on("picker-accessible-previewed", this.onPickerAccessiblePreviewed);
     this.walker.on("picker-accessible-canceled", this.onPickerAccessibleCanceled);
 
     await this.walker.pick(doFocus);
+    this._telemetry.toolOpened("accessibilityPickerUsed");
     this.emit("picker-started");
   }
 
   /**
    * Toggle between starting and canceling the picker.
    * @param  {Boolean} doFocus
    *         If true, move keyboard focus into content.
    */
--- a/devtools/client/framework/devtools.js
+++ b/devtools/client/framework/devtools.js
@@ -720,17 +720,17 @@ DevTools.prototype = {
 
     let toolbox = await gDevTools.showToolbox(
       target, "accessibility", null, null, startTime);
     let nodeFront = await this.findNodeFront(toolbox.walker, nodeSelectors);
     // Select the accessible object in the panel and wait for the event that
     // tells us it has been done.
     let a11yPanel = toolbox.getCurrentPanel();
     let onSelected = a11yPanel.once("new-accessible-front-selected");
-    a11yPanel.selectAccessibleForNode(nodeFront);
+    a11yPanel.selectAccessibleForNode(nodeFront, "browser-context-menu");
     await onSelected;
   },
 
   /**
    * Either the DevTools Loader has been destroyed or firefox is shutting down.
    * @param {boolean} shuttingDown
    *        True if firefox is currently shutting down. We may prevent doing
    *        some cleanups to speed it up. Otherwise everything need to be
--- a/devtools/client/framework/toolbox-options.js
+++ b/devtools/client/framework/toolbox-options.js
@@ -218,16 +218,20 @@ OptionsPanel.prototype = {
     // globally) and per toolbox (for the tools registered to a single toolbox).
     // This event handler expect this to be binded to the related checkbox element.
     let onCheckboxClick = function(tool) {
       // Set the kill switch pref boolean to true
       Services.prefs.setBoolPref(tool.visibilityswitch, this.checked);
 
       if (!tool.isWebExtension) {
         gDevTools.emit(this.checked ? "tool-registered" : "tool-unregistered", tool.id);
+        // Record which tools were registered and unregistered.
+        Services.telemetry.keyedScalarSet("devtools.tool.registered",
+                                          tool.id,
+                                          this.checked);
       }
     };
 
     let createToolCheckbox = (tool) => {
       let checkboxLabel = this.panelDoc.createElement("label");
       let checkboxInput = this.panelDoc.createElement("input");
       checkboxInput.setAttribute("type", "checkbox");
       checkboxInput.setAttribute("id", tool.id);
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -1991,17 +1991,18 @@ Inspector.prototype = {
   /**
    * Show Accessibility properties for currently selected node
    */
   async showAccessibilityProperties() {
     let a11yPanel = await this._toolbox.selectTool("accessibility");
     // Select the accessible object in the panel and wait for the event that
     // tells us it has been done.
     let onSelected = a11yPanel.once("new-accessible-front-selected");
-    a11yPanel.selectAccessibleForNode(this.selection.nodeFront);
+    a11yPanel.selectAccessibleForNode(this.selection.nodeFront,
+                                      "inspector-context-menu");
     await onSelected;
   },
 
   /**
    * Use in Console.
    *
    * Takes the currently selected node in the inspector and assigns it to a
    * temp variable on the content window.  Also opens the split console and
--- a/devtools/client/shared/telemetry.js
+++ b/devtools/client/shared/telemetry.js
@@ -19,16 +19,17 @@ const PENDING_EVENT_PROPERTIES = new Map
 
 class Telemetry {
   constructor() {
     // 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.logScalar = this.logScalar.bind(this);
+    this.logCountScalar = this.logCountScalar.bind(this);
     this.logKeyedScalar = this.logKeyedScalar.bind(this);
     this.logOncePerBrowserVersion = this.logOncePerBrowserVersion.bind(this);
     this.recordEvent = this.recordEvent.bind(this);
     this.setEventRecordingEnabled = this.setEventRecordingEnabled.bind(this);
     this.preparePendingEvent = this.preparePendingEvent.bind(this);
     this.addEventProperty = this.addEventProperty.bind(this);
     this.destroy = this.destroy.bind(this);
 
@@ -182,16 +183,27 @@ class Telemetry {
       gridInspectorShowGridAreasOverlayChecked: {
         scalar: "devtools.grid.showGridAreasOverlay.checked",
       },
       gridInspectorShowGridLineNumbersChecked: {
         scalar: "devtools.grid.showGridLineNumbers.checked",
       },
       gridInspectorShowInfiniteLinesChecked: {
         scalar: "devtools.grid.showInfiniteLines.checked",
+      },
+      accessibility: {
+        countScalar: "devtools.accessibility.opened_count",
+        timerHistogram: "DEVTOOLS_ACCESSIBILITY_TIME_ACTIVE_SECONDS"
+      },
+      accessibilityNodeInspected: {
+        countScalar: "devtools.accessibility.node_inspected_count"
+      },
+      accessibilityPickerUsed: {
+        countScalar: "devtools.accessibility.picker_used_count",
+        timerHistogram: "DEVTOOLS_ACCESSIBILITY_PICKER_TIME_ACTIVE_SECONDS"
       }
     };
   }
 
   /**
    * Add an entry to a histogram.
    *
    * @param  {String} id
@@ -205,16 +217,19 @@ class Telemetry {
       this.log(charts.histogram, true);
     }
     if (charts.timerHistogram) {
       this.startTimer(charts.timerHistogram);
     }
     if (charts.scalar) {
       this.logScalar(charts.scalar, 1);
     }
+    if (charts.countScalar) {
+      this.logCountScalar(charts.countScalar, 1);
+    }
   }
 
   /**
    * Record that an action occurred.  Aliases to `toolOpened`, so it's just for
    * readability at the call site for cases where we aren't actually opening
    * tools.
    */
   actionOccurred(id) {
@@ -296,28 +311,56 @@ class Telemetry {
     if (!scalarId) {
       return;
     }
 
     try {
       if (isNaN(value) && typeof value !== "boolean") {
         dump(`Warning: An attempt was made to write a non-numeric and ` +
              `non-boolean value ${value} to the ${scalarId} scalar. Only ` +
-             `numeric and boolean values are allowed.`);
+             `numeric and boolean values are allowed.\n`);
 
         return;
       }
       Services.telemetry.scalarSet(scalarId, value);
     } catch (e) {
       dump(`Warning: An attempt was made to write to the ${scalarId} ` +
            `scalar, which is not defined in Scalars.yaml\n`);
     }
   }
 
   /**
+   * Log a value to a count scalar.
+   *
+   * @param  {String} scalarId
+   *         Scalar in which the data is to be stored.
+   * @param  value
+   *         Value to store.
+   */
+  logCountScalar(scalarId, value) {
+    if (!scalarId) {
+      return;
+    }
+
+    try {
+      if (isNaN(value)) {
+        dump(`Warning: An attempt was made to write a non-numeric value ` +
+             `${value} to the ${scalarId} scalar. Only numeric values are ` +
+             `allowed.\n`);
+
+        return;
+      }
+      Services.telemetry.scalarAdd(scalarId, value);
+    } catch (e) {
+      dump(`Warning: An attempt was made to write to the ${scalarId} ` +
+           `scalar, which is not defined in Scalars.yaml\n`);
+    }
+  }
+
+  /**
    * Log a value to a keyed count scalar.
    *
    * @param  {String} scalarId
    *         Scalar in which the data is to be stored.
    * @param  {String} key
    *         The key within the  scalar.
    * @param  value
    *         Value to store.
@@ -326,17 +369,17 @@ class Telemetry {
     if (!scalarId) {
       return;
     }
 
     try {
       if (isNaN(value)) {
         dump(`Warning: An attempt was made to write a non-numeric value ` +
              `${value} to the ${scalarId} scalar. Only numeric values are ` +
-             `allowed.`);
+             `allowed.\n`);
 
         return;
       }
       Services.telemetry.keyedScalarAdd(scalarId, key, value);
     } catch (e) {
       dump(`Warning: An attempt was made to write to the ${scalarId} ` +
            `scalar, which is not defined in Scalars.yaml\n`);
     }
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -9453,16 +9453,49 @@
     "record_in_processes": ["main", "content"],
     "alert_emails": ["dev-developer-tools@lists.mozilla.org"],
     "expires_in_version": "never",
     "kind": "count",
     "bug_numbers": [1247985],
     "description": "Number of times a custom developer tool has been opened.",
     "releaseChannelCollection": "opt-out"
   },
+  "DEVTOOLS_ACCESSIBILITY_TIME_ACTIVE_SECONDS": {
+    "record_in_processes": ["main", "content"],
+    "expires_in_version": "65",
+    "kind": "exponential",
+    "high": 86400,
+    "n_buckets": 100,
+    "bug_numbers": [1447302],
+    "alert_emails": ["dev-developer-tools@lists.mozilla.org", "yzenevich@mozilla.com"],
+    "releaseChannelCollection": "opt-out",
+    "description": "How long has the accessibility panel been active (seconds)."
+  },
+  "DEVTOOLS_ACCESSIBILITY_PICKER_TIME_ACTIVE_SECONDS": {
+    "record_in_processes": ["main", "content"],
+    "expires_in_version": "65",
+    "kind": "exponential",
+    "high": 86400,
+    "n_buckets": 100,
+    "bug_numbers": [1447302],
+    "alert_emails": ["dev-developer-tools@lists.mozilla.org", "yzenevich@mozilla.com"],
+    "releaseChannelCollection": "opt-out",
+    "description": "How long has the picker tool in accessibility panel been active (seconds)."
+  },
+  "DEVTOOLS_ACCESSIBILITY_SERVICE_TIME_ACTIVE_SECONDS": {
+    "record_in_processes": ["main", "content"],
+    "expires_in_version": "65",
+    "kind": "exponential",
+    "high": 86400,
+    "n_buckets": 100,
+    "bug_numbers": [1447302],
+    "alert_emails": ["dev-developer-tools@lists.mozilla.org", "yzenevich@mozilla.com"],
+    "releaseChannelCollection": "opt-out",
+    "description": "How long has the platform accessibility been active (seconds) in accessibility panel."
+  },
   "DEVTOOLS_TOOLBOX_TIME_ACTIVE_SECONDS": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 10000000,
     "n_buckets": 100,
     "bug_numbers": [1446496],
     "alert_emails": ["dev-developer-tools@lists.mozilla.org", "jryans@mozilla.com"],
--- a/toolkit/components/telemetry/Scalars.yaml
+++ b/toolkit/components/telemetry/Scalars.yaml
@@ -964,16 +964,112 @@ devtools.responsive:
     kind: uint
     keyed: true
     notification_emails:
       - dev-developer-tools@lists.mozilla.org, jryans@mozilla.com
     record_in_processes:
       - main
     release_channel_collection: opt-out
 
+devtools.tool:
+  registered:
+    bug_numbers:
+      - 1447302
+    description: >
+      Recorded on enable tool checkbox check/uncheck in Developer Tools options
+      panel. Boolean stating if the tool was enabled or disabled by the user.
+      Keyed by tool id. Current default tools with their id's are defined in
+      https://dxr.mozilla.org/mozilla-central/source/devtools/client/definitions.js
+    expires: "65"
+    kind: boolean
+    keyed: true
+    notification_emails:
+      - dev-developer-tools@lists.mozilla.org
+      - yzenevich@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - 'main'
+
+devtools.accessibility:
+  node_inspected_count:
+    bug_numbers:
+      - 1447302
+    description: >
+      Number of times a DOM node was inspected from within the Accessibility
+      tool.
+    expires: "65"
+    kind: uint
+    notification_emails:
+      - dev-developer-tools@lists.mozilla.org
+      - yzenevich@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - 'main'
+
+  opened_count:
+    bug_numbers:
+      - 1447302
+    description: >
+      Number of times the DevTools Accessibility tool has been opened.
+    expires: "65"
+    kind: uint
+    notification_emails:
+      - dev-developer-tools@lists.mozilla.org
+      - yzenevich@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - 'main'
+
+  picker_used_count:
+    bug_numbers:
+      - 1447302
+    description: >
+      Number of times the picker tool has been used in DevTools Accessibility
+      panel.
+    expires: "65"
+    kind: uint
+    notification_emails:
+      - dev-developer-tools@lists.mozilla.org
+      - yzenevich@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - 'main'
+
+  select_accessible_for_node:
+    bug_numbers:
+      - 1447302
+    description: >
+      Number of times an accessible object was inspected from outside the
+      Accessibility tool (navigation to Accessibility panel). Keyed by the
+      source of user action (inspector context menu, browser context menu, etc).
+    expires: "65"
+    kind: uint
+    keyed: true
+    notification_emails:
+      - dev-developer-tools@lists.mozilla.org
+      - yzenevich@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - 'main'
+
+  service_enabled_count:
+    bug_numbers:
+      - 1447302
+    description: >
+      Number of times platform accessibility has been enabled in DevTools
+      Accessibility panel.
+    expires: "65"
+    kind: uint
+    notification_emails:
+      - dev-developer-tools@lists.mozilla.org
+      - yzenevich@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - 'main'
+
 navigator.storage:
   estimate_count:
     bug_numbers:
       - 1359708
     description: >
       Number of times navigator.storage.estimate has been used.
     expires: "60"
     kind: uint