Bug 1178964 - Track editing actions in WebIDE with telemetry. r=ochameau a=sylvestre
authorJ. Ryan Stinnett <jryans@gmail.com>
Mon, 17 Aug 2015 18:44:13 -0500
changeset 288841 bf5fb167c2f51eeb94e6f3340c22995625a549e2
parent 288840 c11fa31e5fc1cf899d683b29dd27a92d3b4ee67b
child 288842 70da6494624c0198b50517df146649d3944467c4
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersochameau, sylvestre
bugs1178964
milestone42.0a2
Bug 1178964 - Track editing actions in WebIDE with telemetry. r=ochameau a=sylvestre
browser/devtools/shared/telemetry.js
browser/devtools/webide/content/webide.js
browser/devtools/webide/modules/project-list.js
browser/devtools/webide/test/sidebars/test_telemetry.html
browser/devtools/webide/test/test_telemetry.html
toolkit/components/telemetry/Histograms.json
--- a/browser/devtools/shared/telemetry.js
+++ b/browser/devtools/shared/telemetry.js
@@ -146,19 +146,19 @@ Telemetry.prototype = {
       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"
     },
     storage: {
-       histogram: "DEVTOOLS_STORAGE_OPENED_BOOLEAN",
-       userHistogram: "DEVTOOLS_STORAGE_OPENED_PER_USER_FLAG",
-       timerHistogram: "DEVTOOLS_STORAGE_TIME_ACTIVE_SECONDS"
+      histogram: "DEVTOOLS_STORAGE_OPENED_BOOLEAN",
+      userHistogram: "DEVTOOLS_STORAGE_OPENED_PER_USER_FLAG",
+      timerHistogram: "DEVTOOLS_STORAGE_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",
@@ -192,16 +192,33 @@ Telemetry.prototype = {
       userHistogram: "DEVTOOLS_DEVELOPERTOOLBAR_OPENED_PER_USER_FLAG",
       timerHistogram: "DEVTOOLS_DEVELOPERTOOLBAR_TIME_ACTIVE_SECONDS"
     },
     webide: {
       histogram: "DEVTOOLS_WEBIDE_OPENED_BOOLEAN",
       userHistogram: "DEVTOOLS_WEBIDE_OPENED_PER_USER_FLAG",
       timerHistogram: "DEVTOOLS_WEBIDE_TIME_ACTIVE_SECONDS"
     },
+    webideProjectEditor: {
+      histogram: "DEVTOOLS_WEBIDE_PROJECT_EDITOR_OPENED_BOOLEAN",
+      userHistogram: "DEVTOOLS_WEBIDE_PROJECT_EDITOR_OPENED_PER_USER_FLAG",
+      timerHistogram: "DEVTOOLS_WEBIDE_PROJECT_EDITOR_TIME_ACTIVE_SECONDS"
+    },
+    webideProjectEditorSave: {
+      histogram: "DEVTOOLS_WEBIDE_PROJECT_EDITOR_SAVE_BOOLEAN",
+      userHistogram: "DEVTOOLS_WEBIDE_PROJECT_EDITOR_SAVE_PER_USER_FLAG",
+    },
+    webideNewProject: {
+      histogram: "DEVTOOLS_WEBIDE_NEW_PROJECT_BOOLEAN",
+      userHistogram: "DEVTOOLS_WEBIDE_NEW_PROJECT_PER_USER_FLAG",
+    },
+    webideImportProject: {
+      histogram: "DEVTOOLS_WEBIDE_IMPORT_PROJECT_BOOLEAN",
+      userHistogram: "DEVTOOLS_WEBIDE_IMPORT_PROJECT_PER_USER_FLAG",
+    },
     custom: {
       histogram: "DEVTOOLS_CUSTOM_OPENED_BOOLEAN",
       userHistogram: "DEVTOOLS_CUSTOM_OPENED_PER_USER_FLAG",
       timerHistogram: "DEVTOOLS_CUSTOM_TIME_ACTIVE_SECONDS"
     }
   },
 
   /**
@@ -220,16 +237,25 @@ Telemetry.prototype = {
     if (charts.userHistogram) {
       this.logOncePerBrowserVersion(charts.userHistogram, true);
     }
     if (charts.timerHistogram) {
       this.startTimer(charts.timerHistogram);
     }
   },
 
+  /**
+   * 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) {
+    this.toolOpened(id);
+  },
+
   toolClosed: function(id) {
     let charts = this._histograms[id];
 
     if (!charts || !charts.timerHistogram) {
       return;
     }
 
     this.stopTimer(charts.timerHistogram);
--- a/browser/devtools/webide/content/webide.js
+++ b/browser/devtools/webide/content/webide.js
@@ -142,16 +142,18 @@ let UI = {
     AppManager.off("app-manager-update", this.appManagerUpdate);
     AppManager.destroy();
     Simulators.off("configure", this.configureSimulator);
     projectList.destroy();
     runtimeList.destroy();
     window.removeEventListener("message", this.onMessage);
     this.updateConnectionTelemetry();
     this._telemetry.toolClosed("webide");
+    this._telemetry.toolClosed("webideProjectEditor");
+    this._telemetry.destroy();
   },
 
   canCloseProject: function() {
     if (this.projecteditor) {
       return this.projecteditor.confirmUnsaved();
     }
     return true;
   },
@@ -665,39 +667,45 @@ let UI = {
 
   destroyProjectEditor: function() {
     if (this.projecteditor) {
       this.projecteditor.destroy();
       this.projecteditor = null;
     }
   },
 
-  updateProjectEditorMenusVisibility: function() {
+  /**
+   * Called when selecting or deselecting the project editor panel.
+   */
+  onChangeProjectEditorSelected: function() {
     if (this.projecteditor) {
       let panel = document.querySelector("#deck").selectedPanel;
       if (panel && panel.id == "deck-panel-projecteditor") {
         this.projecteditor.menuEnabled = true;
+        this._telemetry.toolOpened("webideProjectEditor");
       } else {
         this.projecteditor.menuEnabled = false;
+        this._telemetry.toolClosed("webideProjectEditor");
       }
     }
   },
 
   getProjectEditor: function() {
     if (this.projecteditor) {
       return this.projecteditor.loaded;
     }
 
     let projecteditorIframe = document.querySelector("#deck-panel-projecteditor");
     this.projecteditor = ProjectEditor.ProjectEditor(projecteditorIframe, {
       menubar: document.querySelector("#main-menubar"),
       menuindex: 1
     });
-    this.projecteditor.on("onEditorSave", (editor, resource) => {
+    this.projecteditor.on("onEditorSave", () => {
       AppManager.validateAndUpdateProject(AppManager.selectedProject);
+      this._telemetry.actionOccurred("webideProjectEditorSave");
     });
     return this.projecteditor.loaded;
   },
 
   updateProjectEditorHeader: function() {
     let project = AppManager.selectedProject;
     if (!project || !this.projecteditor) {
       return;
@@ -743,21 +751,21 @@ let UI = {
         !this.isProjectEditorEnabled() ||
         forceDetailsOnly) {
       this.selectDeckPanel("details");
       return;
     }
 
     // Show ProjectEditor
 
-    this.selectDeckPanel("projecteditor");
-
     this.getProjectEditor().then(() => {
       this.updateProjectEditorHeader();
     }, console.error);
+
+    this.selectDeckPanel("projecteditor");
   },
 
   autoStartProject: Task.async(function*() {
     let project = AppManager.selectedProject;
 
     if (!project) {
       return;
     }
@@ -801,16 +809,18 @@ let UI = {
         project = AppProjects.get(isPackaged ? source.path : source);
       } else {
         throw e;
       }
     }
 
     // Select project
     AppManager.selectedProject = project;
+
+    this._telemetry.actionOccurred("webideImportProject");
   }),
 
   // Remember the last selected project on the runtime
   saveLastSelectedProject: function() {
     let shouldRestore = Services.prefs.getBoolPref("devtools.webide.restoreLastProject");
     if (!shouldRestore) {
       return;
     }
@@ -914,25 +924,25 @@ let UI = {
     this.resetFocus();
     let panel = deck.querySelector("#deck-panel-" + id);
     let lazysrc = panel.getAttribute("lazysrc");
     if (lazysrc) {
       panel.removeAttribute("lazysrc");
       panel.setAttribute("src", lazysrc);
     }
     deck.selectedPanel = panel;
-    this.updateProjectEditorMenusVisibility();
+    this.onChangeProjectEditorSelected();
     this.updateToolboxFullscreenState();
   },
 
   resetDeck: function() {
     this.resetFocus();
     let deck = document.querySelector("#deck");
     deck.selectedPanel = null;
-    this.updateProjectEditorMenusVisibility();
+    this.onChangeProjectEditorSelected();
   },
 
   buildIDToDate(buildID) {
     let fields = buildID.match(/(\d{4})(\d{2})(\d{2})/);
     // Date expects 0 - 11 for months
     return new Date(fields[1], Number.parseInt(fields[2]) - 1, fields[3]);
   },
 
--- a/browser/devtools/webide/modules/project-list.js
+++ b/browser/devtools/webide/modules/project-list.js
@@ -6,28 +6,30 @@ const {Cu} = require("chrome");
 
 const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {AppProjects} = require("devtools/app-manager/app-projects");
 const {AppManager} = require("devtools/webide/app-manager");
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const EventEmitter = require("devtools/toolkit/event-emitter");
 const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
 const utils = require("devtools/webide/utils");
+const Telemetry = require("devtools/shared/telemetry");
 
 const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/webide.properties");
 
 let ProjectList;
 
 module.exports = ProjectList = function(win, parentWindow) {
   EventEmitter.decorate(this);
   this._doc = win.document;
   this._UI = parentWindow.UI;
   this._parentWindow = parentWindow;
   this._panelNodeEl = "toolbarbutton";
   this._sidebarsEnabled = Services.prefs.getBoolPref("devtools.webide.sidebars");
+  this._telemetry = new Telemetry();
 
   if (this._sidebarsEnabled) {
     this._panelNodeEl = "div";
   }
 
   this.onWebIDEUpdate = this.onWebIDEUpdate.bind(this);
   this._UI.on("webide-update", this.onWebIDEUpdate);
 
@@ -72,28 +74,31 @@ ProjectList.prototype = {
    * testOptions: {       chrome mochitest support
    *   folder: nsIFile,   where to store the app
    *   index: Number,     index of the app in the template list
    *   name: String       name of the app
    * }
    */
   newApp: function(testOptions) {
     let parentWindow = this._parentWindow;
+    let self = this;
     return this._UI.busyUntil(Task.spawn(function*() {
       // Open newapp.xul, which will feed ret.location
       let ret = {location: null, testOptions: testOptions};
       parentWindow.openDialog("chrome://webide/content/newapp.xul", "newapp", "chrome,modal", ret);
       if (!ret.location)
         return;
 
       // Retrieve added project
       let project = AppProjects.get(ret.location);
 
       // Select project
       AppManager.selectedProject = project;
+
+      self._telemetry.actionOccurred("webideNewProject");
     }), "creating new app");
   },
 
   importPackagedApp: function(location) {
     let parentWindow = this._parentWindow;
     let UI = this._UI;
     return UI.busyUntil(Task.spawn(function*() {
       let directory = utils.getPackagedDirectory(parentWindow, location);
--- a/browser/devtools/webide/test/sidebars/test_telemetry.html
+++ b/browser/devtools/webide/test/sidebars/test_telemetry.html
@@ -158,35 +158,45 @@
           startConnection(win, docRuntime, type, index);
           yield waitUntilConnected(win);
         });
       }
 
       function checkResults() {
         let result = Telemetry.prototype.telemetryInfo;
         for (let [histId, value] of Iterator(result)) {
-          if (histId.endsWith("OPENED_PER_USER_FLAG")) {
+          if (histId.endsWith("_PER_USER_FLAG")) {
             ok(value.length === 1 && !!value[0],
                "Per user value " + histId + " has a single value of true");
-          } else if (histId.endsWith("OPENED_BOOLEAN")) {
+          } else if (histId === "DEVTOOLS_WEBIDE_IMPORT_PROJECT_BOOLEAN") {
+            ok(value.length === 1 && !!value[0],
+               histId + " has 1 successful entry");
+          } else if (histId ===
+                     "DEVTOOLS_WEBIDE_PROJECT_EDITOR_OPENED_BOOLEAN") {
+            ok(value.length === 1 && !!value[0],
+               histId + " has 1 successful entry");
+          } else if (histId === "DEVTOOLS_WEBIDE_OPENED_BOOLEAN") {
             ok(value.length > 1, histId + " has more than one entry");
 
             let okay = value.every(function(element) {
               return !!element;
             });
 
             ok(okay, "All " + histId + " entries are true");
-          } else if (histId.endsWith("TIME_ACTIVE_SECONDS")) {
+          } else if (histId.endsWith("WEBIDE_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");
+          } else if (histId.endsWith("EDITOR_TIME_ACTIVE_SECONDS")) {
+            ok(value.length === 1 && value[0] > 0,
+               histId + " has 1 entry with time > 0");
           } else if (histId === "DEVTOOLS_WEBIDE_CONNECTION_RESULT") {
             ok(value.length === 6, histId + " has 6 connection results");
 
             let okay = value.every(function(element) {
               return !!element;
             });
 
             ok(okay, "All " + histId + " connections succeeded");
--- a/browser/devtools/webide/test/test_telemetry.html
+++ b/browser/devtools/webide/test/test_telemetry.html
@@ -158,35 +158,45 @@
           startConnection(win, type, index);
           yield waitUntilConnected(win);
         });
       }
 
       function checkResults() {
         let result = Telemetry.prototype.telemetryInfo;
         for (let [histId, value] of Iterator(result)) {
-          if (histId.endsWith("OPENED_PER_USER_FLAG")) {
+          if (histId.endsWith("_PER_USER_FLAG")) {
             ok(value.length === 1 && !!value[0],
                "Per user value " + histId + " has a single value of true");
-          } else if (histId.endsWith("OPENED_BOOLEAN")) {
+          } else if (histId === "DEVTOOLS_WEBIDE_IMPORT_PROJECT_BOOLEAN") {
+            ok(value.length === 1 && !!value[0],
+               histId + " has 1 successful entry");
+          } else if (histId ===
+                     "DEVTOOLS_WEBIDE_PROJECT_EDITOR_OPENED_BOOLEAN") {
+            ok(value.length === 1 && !!value[0],
+               histId + " has 1 successful entry");
+          } else if (histId === "DEVTOOLS_WEBIDE_OPENED_BOOLEAN") {
             ok(value.length > 1, histId + " has more than one entry");
 
             let okay = value.every(function(element) {
               return !!element;
             });
 
             ok(okay, "All " + histId + " entries are true");
-          } else if (histId.endsWith("TIME_ACTIVE_SECONDS")) {
+          } else if (histId.endsWith("WEBIDE_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");
+          } else if (histId.endsWith("EDITOR_TIME_ACTIVE_SECONDS")) {
+            ok(value.length === 1 && value[0] > 0,
+               histId + " has 1 entry with time > 0");
           } else if (histId === "DEVTOOLS_WEBIDE_CONNECTION_RESULT") {
             ok(value.length === 6, histId + " has 6 connection results");
 
             let okay = value.every(function(element) {
               return !!element;
             });
 
             ok(okay, "All " + histId + " connections succeeded");
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -6683,16 +6683,36 @@
     "kind": "boolean",
     "description": "How many times has the devtool's Developer Toolbar been opened via the toolbox button?"
   },
   "DEVTOOLS_WEBIDE_OPENED_BOOLEAN": {
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "How many times has the DevTools WebIDE been opened?"
   },
+  "DEVTOOLS_WEBIDE_PROJECT_EDITOR_OPENED_BOOLEAN": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "description": "How many times has the DevTools WebIDE project editor been opened?"
+  },
+  "DEVTOOLS_WEBIDE_PROJECT_EDITOR_SAVE_BOOLEAN": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "description": "How many times has a file been saved in the DevTools WebIDE project editor?"
+  },
+  "DEVTOOLS_WEBIDE_NEW_PROJECT_BOOLEAN": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "description": "How many times has a new project been created in the DevTools WebIDE?"
+  },
+  "DEVTOOLS_WEBIDE_IMPORT_PROJECT_BOOLEAN": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "description": "How many times has a project been imported into the DevTools WebIDE?"
+  },
   "DEVTOOLS_CUSTOM_OPENED_BOOLEAN": {
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "How many times has a custom developer tool been opened via the toolbox button?"
   },
   "DEVTOOLS_TOOLBOX_OPENED_PER_USER_FLAG": {
     "expires_in_version": "never",
     "kind": "flag",
@@ -6828,16 +6848,36 @@
     "kind": "flag",
     "description": "How many users have opened the devtool's Developer Toolbar been opened via the toolbox button?"
   },
   "DEVTOOLS_WEBIDE_OPENED_PER_USER_FLAG": {
     "expires_in_version": "never",
     "kind": "flag",
     "description": "How many users have opened the DevTools WebIDE?"
   },
+  "DEVTOOLS_WEBIDE_PROJECT_EDITOR_OPENED_PER_USER_FLAG": {
+    "expires_in_version": "never",
+    "kind": "flag",
+    "description": "How many users have opened the DevTools WebIDE project editor?"
+  },
+  "DEVTOOLS_WEBIDE_PROJECT_EDITOR_SAVE_PER_USER_FLAG": {
+    "expires_in_version": "never",
+    "kind": "flag",
+    "description": "How many users have saved a file in the DevTools WebIDE project editor?"
+  },
+  "DEVTOOLS_WEBIDE_NEW_PROJECT_PER_USER_FLAG": {
+    "expires_in_version": "never",
+    "kind": "flag",
+    "description": "How many users have created a new project in the DevTools WebIDE?"
+  },
+  "DEVTOOLS_WEBIDE_IMPORT_PROJECT_PER_USER_FLAG": {
+    "expires_in_version": "never",
+    "kind": "flag",
+    "description": "How many users have imported a project into the DevTools WebIDE?"
+  },
   "DEVTOOLS_CUSTOM_OPENED_PER_USER_FLAG": {
     "expires_in_version": "never",
     "kind": "flag",
     "description": "How many users have opened a custom developer tool via the toolbox button?"
   },
   "DEVTOOLS_TOOLBOX_TIME_ACTIVE_SECONDS": {
     "expires_in_version": "never",
     "kind": "exponential",
@@ -7008,16 +7048,23 @@
   },
   "DEVTOOLS_WEBIDE_TIME_ACTIVE_SECONDS": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "10000000",
     "n_buckets": 100,
     "description": "How long has WebIDE been active (seconds)"
   },
+  "DEVTOOLS_WEBIDE_PROJECT_EDITOR_TIME_ACTIVE_SECONDS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "10000000",
+    "n_buckets": 100,
+    "description": "How long has WebIDE's project editor been active (seconds)"
+  },
   "DEVTOOLS_CUSTOM_TIME_ACTIVE_SECONDS": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "10000000",
     "n_buckets": 100,
     "description": "How long has a custom developer tool been active (seconds)"
   },
   "DEVTOOLS_WEBIDE_CONNECTION_RESULT": {