Merge m-c to fx-team, a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 26 Jul 2016 16:55:01 -0700
changeset 348871 d9290a592a0d6ab421c9a109d42bcb140ceb8db9
parent 348870 951aa830b05185b7a7c98bf734dbab355b31a915 (diff)
parent 348831 462dc6b44adb4573e8ce6b0dd688c206ebd516f7 (current diff)
child 348872 820cfffb1fe2b87a09d1c57d06d02107be8ac778
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.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
Merge m-c to fx-team, a=merge
dom/tests/mochitest/beacon/beacon-cors-redirect-handler.sjs
dom/tests/mochitest/beacon/test_beaconCORSRedirect.html
dom/tests/mochitest/beacon/test_beaconPreflight.html
dom/tests/mochitest/beacon/test_beaconPreflightFailure.html
--- a/.eslintignore
+++ b/.eslintignore
@@ -82,17 +82,20 @@ devtools/client/canvasdebugger/**
 devtools/client/commandline/**
 devtools/client/debugger/**
 devtools/client/eyedropper/**
 devtools/client/framework/**
 devtools/client/jsonview/lib/**
 devtools/client/memory/**
 devtools/client/netmonitor/test/**
 devtools/client/netmonitor/har/test/**
-devtools/client/performance/**
+devtools/client/performance/components/**
+devtools/client/performance/legacy/**
+devtools/client/performance/modules/**
+devtools/client/performance/test/**
 devtools/client/projecteditor/**
 devtools/client/promisedebugger/**
 devtools/client/responsivedesign/**
 devtools/client/scratchpad/**
 devtools/client/shadereditor/**
 devtools/client/shared/*.jsm
 devtools/client/shared/webgl-utils.js
 devtools/client/shared/developer-toolbar.js
--- a/devtools/client/performance/panel.js
+++ b/devtools/client/performance/panel.js
@@ -1,16 +1,15 @@
 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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 { Cc, Ci, Cu, Cr } = require("chrome");
 const { Task } = require("devtools/shared/task");
 
 loader.lazyRequireGetter(this, "promise");
 loader.lazyRequireGetter(this, "EventEmitter",
   "devtools/shared/event-emitter");
 
 function PerformancePanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
--- a/devtools/client/performance/performance-controller.js
+++ b/devtools/client/performance/performance-controller.js
@@ -1,43 +1,51 @@
 /* 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";
 
+/* globals document, PerformanceView, ToolbarView, RecordingsView, DetailsView */
+
+/* exported Cc, Ci, Cu, Cr, loader */
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 var BrowserLoaderModule = {};
 Cu.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule);
 var { loader, require } = BrowserLoaderModule.BrowserLoader({
   baseURI: "resource://devtools/client/performance/",
   window: this
 });
 var { Task } = require("devtools/shared/task");
+/* exported Heritage, ViewHelpers, WidgetMethods, setNamedTimeout, clearNamedTimeout */
 var { Heritage, ViewHelpers, WidgetMethods, setNamedTimeout, clearNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
 var { gDevTools } = require("devtools/client/framework/devtools");
 
 // Events emitted by various objects in the panel.
 var EVENTS = require("devtools/client/performance/events");
 Object.defineProperty(this, "EVENTS", {
   value: EVENTS,
   enumerable: true,
   writable: false
 });
 
+/* exported React, ReactDOM, JITOptimizationsView, Services, promise, EventEmitter,
+   DevToolsUtils, system */
 var React = require("devtools/client/shared/vendor/react");
 var ReactDOM = require("devtools/client/shared/vendor/react-dom");
 var JITOptimizationsView = React.createFactory(require("devtools/client/performance/components/jit-optimizations"));
 var Services = require("Services");
 var promise = require("promise");
 var EventEmitter = require("devtools/shared/event-emitter");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var system = require("devtools/shared/system");
 
 // Logic modules
-
+/* exported L10N, PerformanceTelemetry, TIMELINE_BLUEPRINT, RecordingUtils,
+   OptimizationsGraph, GraphsController, WaterfallHeader, MarkerView, MarkerDetails,
+   MarkerBlueprintUtils, WaterfallUtils, FrameUtils, CallView, ThreadNode, FrameNode */
 var { L10N } = require("devtools/client/performance/modules/global");
 var { PerformanceTelemetry } = require("devtools/client/performance/modules/logic/telemetry");
 var { TIMELINE_BLUEPRINT } = require("devtools/client/performance/modules/markers");
 var RecordingUtils = require("devtools/shared/performance/recording-utils");
 var { OptimizationsGraph, GraphsController } = require("devtools/client/performance/modules/widgets/graphs");
 var { WaterfallHeader } = require("devtools/client/performance/modules/widgets/waterfall-ticks");
 var { MarkerView } = require("devtools/client/performance/modules/widgets/marker-view");
 var { MarkerDetails } = require("devtools/client/performance/modules/widgets/marker-details");
@@ -45,29 +53,33 @@ var { MarkerBlueprintUtils } = require("
 var WaterfallUtils = require("devtools/client/performance/modules/logic/waterfall-utils");
 var FrameUtils = require("devtools/client/performance/modules/logic/frame-utils");
 var { CallView } = require("devtools/client/performance/modules/widgets/tree-view");
 var { ThreadNode } = require("devtools/client/performance/modules/logic/tree-model");
 var { FrameNode } = require("devtools/client/performance/modules/logic/tree-model");
 
 // Widgets modules
 
+/* exported OptionsView, FlameGraph, FlameGraphUtils, TreeWidget, SideMenuWidget */
 var { OptionsView } = require("devtools/client/shared/options-view");
 var { FlameGraph, FlameGraphUtils } = require("devtools/client/shared/widgets/FlameGraph");
 var { TreeWidget } = require("devtools/client/shared/widgets/TreeWidget");
-
 var { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
 
+/* exported BRANCH_NAME */
 var BRANCH_NAME = "devtools.performance.ui.";
 
 /**
  * The current target, toolbox and PerformanceFront, set by this tool's host.
  */
+/* exported gToolbox, gTarget, gFront */
 var gToolbox, gTarget, gFront;
 
+/* exported startupPerformance, shutdownPerformance, PerformanceController */
+
 /**
  * Initializes the profiler controller and views.
  */
 var startupPerformance = Task.async(function* () {
   yield PerformanceController.initialize();
   yield PerformanceView.initialize();
   PerformanceController.enableFrontEventListeners();
 });
@@ -278,18 +290,19 @@ var PerformanceController = {
    *        The file to stream the data into.
    */
   exportRecording: Task.async(function* (_, recording, file) {
     yield recording.exportRecording(file);
     this.emit(EVENTS.RECORDING_EXPORTED, recording, file);
   }),
 
    /**
-   * Clears all completed recordings from the list as well as the current non-console recording.
-   * Emits `EVENTS.RECORDING_DELETED` when complete so other components can clean up.
+   * Clears all completed recordings from the list as well as the current non-console
+   * recording. Emits `EVENTS.RECORDING_DELETED` when complete so other components can
+   * clean up.
    */
   clearRecordings: Task.async(function* () {
     for (let i = this._recordings.length - 1; i >= 0; i--) {
       let model = this._recordings[i];
       if (!model.isConsole() && model.isRecording()) {
         yield this.stopRecording();
       }
       // If last recording is not recording, but finalizing itself,
@@ -303,18 +316,17 @@ var PerformanceController = {
         this.emit(EVENTS.RECORDING_DELETED, model);
         this._recordings.splice(i, 1);
       }
     }
     if (this._recordings.length > 0) {
       if (!this._recordings.includes(this.getCurrentRecording())) {
         this.setCurrentRecording(this._recordings[0]);
       }
-    }
-    else {
+    } else {
       this.setCurrentRecording(null);
     }
   }),
 
   /**
    * Loads a recording from a file, adding it to the recordings list. Emits
    * `EVENTS.RECORDING_IMPORTED` when the file was loaded.
    *
@@ -453,18 +465,18 @@ var PerformanceController = {
   },
 
   /**
    * Utility method taking a string or an array of strings of feature names (like
    * "withAllocations" or "withMarkers"), and returns whether or not the current
    * recording supports that feature, based off of UI preferences and server support.
    *
    * @option {Array<string>|string} features
-   *         A string or array of strings indicating what configuration is needed on the recording
-   *         model, like `withTicks`, or `withMemory`.
+   *         A string or array of strings indicating what configuration is needed on the
+   *         recording model, like `withTicks`, or `withMemory`.
    *
    * @return boolean
    */
   isFeatureSupported: function (features) {
     if (!features) {
       return true;
     }
 
@@ -537,19 +549,18 @@ var PerformanceController = {
    * Called on init, sets an `e10s` attribute on the main view container with
    * "disabled" if e10s is possible on the platform and just not on, or "unsupported"
    * if e10s is not possible on the platform. If e10s is on, no attribute is set.
    */
   _setMultiprocessAttributes: function () {
     let { enabled, supported } = this.getMultiprocessStatus();
     if (!enabled && supported) {
       $("#performance-view").setAttribute("e10s", "disabled");
-    }
-    // Could be a chance where the directive goes away yet e10s is still on
-    else if (!enabled && !supported) {
+    } else if (!enabled && !supported) {
+      // Could be a chance where the directive goes away yet e10s is still on
       $("#performance-view").setAttribute("e10s", "unsupported");
     }
   },
 
   /**
    * Pipes an event from some source to the PerformanceController.
    */
   _pipe: function (eventName, ...data) {
@@ -562,14 +573,15 @@ var PerformanceController = {
 /**
  * Convenient way of emitting events from the controller.
  */
 EventEmitter.decorate(PerformanceController);
 
 /**
  * DOM query helpers.
  */
+/* exported $, $$ */
 function $(selector, target = document) {
   return target.querySelector(selector);
 }
 function $$(selector, target = document) {
   return target.querySelectorAll(selector);
 }
--- a/devtools/client/performance/performance-view.js
+++ b/devtools/client/performance/performance-view.js
@@ -1,55 +1,119 @@
 /* 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/. */
 /* import-globals-from performance-controller.js */
+/* globals OverviewView, window */
 "use strict";
-
 /**
  * Master view handler for the performance tool.
  */
 var PerformanceView = {
 
   _state: null,
 
   // Set to true if the front emits a "buffer-status" event, indicating
   // that the server has support for determining buffer status.
   _bufferStatusSupported: false,
 
   // Mapping of state to selectors for different properties and their values,
   // from the main profiler view. Used in `PerformanceView.setState()`
   states: {
     "unavailable": [
-      { sel: "#performance-view", opt: "selectedPanel", val: () => $("#unavailable-notice") },
-      { sel: "#performance-view-content", opt: "hidden", val: () => true },
+      {
+        sel: "#performance-view",
+        opt: "selectedPanel",
+        val: () => $("#unavailable-notice")
+      },
+      {
+        sel: "#performance-view-content",
+        opt: "hidden",
+        val: () => true
+      },
     ],
     "empty": [
-      { sel: "#performance-view", opt: "selectedPanel", val: () => $("#empty-notice") },
-      { sel: "#performance-view-content", opt: "hidden", val: () => true },
+      {
+        sel: "#performance-view",
+        opt: "selectedPanel",
+        val: () => $("#empty-notice")
+      },
+      {
+        sel: "#performance-view-content",
+        opt: "hidden",
+        val: () => true
+      },
     ],
     "recording": [
-      { sel: "#performance-view", opt: "selectedPanel", val: () => $("#performance-view-content") },
-      { sel: "#performance-view-content", opt: "hidden", val: () => false },
-      { sel: "#details-pane-container", opt: "selectedPanel", val: () => $("#recording-notice") },
+      {
+        sel: "#performance-view",
+        opt: "selectedPanel",
+        val: () => $("#performance-view-content")
+      },
+      {
+        sel: "#performance-view-content",
+        opt: "hidden",
+        val: () => false
+      },
+      {
+        sel: "#details-pane-container",
+        opt: "selectedPanel",
+        val: () => $("#recording-notice")
+      },
     ],
     "console-recording": [
-      { sel: "#performance-view", opt: "selectedPanel", val: () => $("#performance-view-content") },
-      { sel: "#performance-view-content", opt: "hidden", val: () => false },
-      { sel: "#details-pane-container", opt: "selectedPanel", val: () => $("#console-recording-notice") },
+      {
+        sel: "#performance-view",
+        opt: "selectedPanel",
+        val: () => $("#performance-view-content")
+      },
+      {
+        sel: "#performance-view-content",
+        opt: "hidden",
+        val: () => false
+      },
+      {
+        sel: "#details-pane-container",
+        opt: "selectedPanel",
+        val: () => $("#console-recording-notice")
+      },
     ],
     "recorded": [
-      { sel: "#performance-view", opt: "selectedPanel", val: () => $("#performance-view-content") },
-      { sel: "#performance-view-content", opt: "hidden", val: () => false },
-      { sel: "#details-pane-container", opt: "selectedPanel", val: () => $("#details-pane") },
+      {
+        sel: "#performance-view",
+        opt: "selectedPanel",
+        val: () => $("#performance-view-content")
+      },
+      {
+        sel: "#performance-view-content",
+        opt: "hidden",
+        val: () => false
+      },
+      {
+        sel: "#details-pane-container",
+        opt: "selectedPanel",
+        val: () => $("#details-pane")
+      },
     ],
     "loading": [
-      { sel: "#performance-view", opt: "selectedPanel", val: () => $("#performance-view-content") },
-      { sel: "#performance-view-content", opt: "hidden", val: () => false },
-      { sel: "#details-pane-container", opt: "selectedPanel", val: () => $("#loading-notice") },
+      {
+        sel: "#performance-view",
+        opt: "selectedPanel",
+        val: () => $("#performance-view-content")
+      },
+      {
+        sel: "#performance-view-content",
+        opt: "hidden",
+        val: () => false
+      },
+      {
+        sel: "#details-pane-container",
+        opt: "selectedPanel",
+        val: () => $("#loading-notice")
+      },
     ]
   },
 
   /**
    * Sets up the view with event binding and main subviews.
    */
   initialize: Task.async(function* () {
     this._recordButton = $("#main-record-button");
@@ -67,20 +131,22 @@ var PerformanceView = {
     for (let button of $$(".record-button")) {
       button.addEventListener("click", this._onRecordButtonClick);
     }
     this._importButton.addEventListener("click", this._onImportButtonClick);
     this._clearButton.addEventListener("click", this._onClearButtonClick);
 
     // Bind to controller events to unlock the record button
     PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
-    PerformanceController.on(EVENTS.RECORDING_PROFILER_STATUS_UPDATE, this._onProfilerStatusUpdated);
+    PerformanceController.on(EVENTS.RECORDING_PROFILER_STATUS_UPDATE,
+                             this._onProfilerStatusUpdated);
     PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE, this._onRecordingStateChange);
     PerformanceController.on(EVENTS.RECORDING_ADDED, this._onRecordingStateChange);
-    PerformanceController.on(EVENTS.BACKEND_FAILED_AFTER_RECORDING_START, this._onNewRecordingFailed);
+    PerformanceController.on(EVENTS.BACKEND_FAILED_AFTER_RECORDING_START,
+                             this._onNewRecordingFailed);
 
     if (yield PerformanceController.canCurrentlyRecord()) {
       this.setState("empty");
     } else {
       this.setState("unavailable");
     }
 
     // Initialize the ToolbarView first, because other views may need access
@@ -97,35 +163,38 @@ var PerformanceView = {
   destroy: Task.async(function* () {
     for (let button of $$(".record-button")) {
       button.removeEventListener("click", this._onRecordButtonClick);
     }
     this._importButton.removeEventListener("click", this._onImportButtonClick);
     this._clearButton.removeEventListener("click", this._onClearButtonClick);
 
     PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
-    PerformanceController.off(EVENTS.RECORDING_PROFILER_STATUS_UPDATE, this._onProfilerStatusUpdated);
-    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE, this._onRecordingStateChange);
+    PerformanceController.off(EVENTS.RECORDING_PROFILER_STATUS_UPDATE,
+                              this._onProfilerStatusUpdated);
+    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE,
+                              this._onRecordingStateChange);
     PerformanceController.off(EVENTS.RECORDING_ADDED, this._onRecordingStateChange);
-    PerformanceController.off(EVENTS.BACKEND_FAILED_AFTER_RECORDING_START, this._onNewRecordingFailed);
+    PerformanceController.off(EVENTS.BACKEND_FAILED_AFTER_RECORDING_START,
+                              this._onNewRecordingFailed);
 
     yield ToolbarView.destroy();
     yield RecordingsView.destroy();
     yield OverviewView.destroy();
     yield DetailsView.destroy();
   }),
 
   /**
    * Sets the state of the profiler view. Possible options are "unavailable",
    * "empty", "recording", "console-recording", "recorded".
    */
   setState: function (state) {
     // Make sure that the focus isn't captured on a hidden iframe. This fixes a
     // XUL bug where shortcuts stop working.
-    const iframes = window.document.querySelectorAll('iframe');
+    const iframes = window.document.querySelectorAll("iframe");
     for (let iframe of iframes) {
       iframe.blur();
     }
     window.focus();
 
     let viewConfig = this.states[state];
     if (!viewConfig) {
       throw new Error(`Invalid state for PerformanceView: ${state}`);
@@ -279,17 +348,18 @@ var PerformanceView = {
     }
   },
 
   /**
    * Handler for clicking the import button.
    */
   _onImportButtonClick: function (e) {
     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
-    fp.init(window, L10N.getStr("recordingsList.importDialogTitle"), Ci.nsIFilePicker.modeOpen);
+    fp.init(window, L10N.getStr("recordingsList.importDialogTitle"),
+            Ci.nsIFilePicker.modeOpen);
     fp.appendFilter(L10N.getStr("recordingsList.saveDialogJSONFilter"), "*.json");
     fp.appendFilter(L10N.getStr("recordingsList.saveDialogAllFilter"), "*.*");
 
     if (fp.show() == Ci.nsIFilePicker.returnOK) {
       this.emit(EVENTS.UI_IMPORT_RECORDING, fp.file);
     }
   },
 
--- a/devtools/client/performance/views/details-abstract-subview.js
+++ b/devtools/client/performance/views/details-abstract-subview.js
@@ -1,53 +1,60 @@
 /* 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/. */
 /* import-globals-from ../performance-controller.js */
 /* import-globals-from ../performance-view.js */
+/* exported DetailsSubview */
 "use strict";
 
 /**
  * A base class from which all detail views inherit.
  */
 var DetailsSubview = {
   /**
    * Sets up the view with event binding.
    */
   initialize: function () {
     this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
     this._onOverviewRangeChange = this._onOverviewRangeChange.bind(this);
     this._onDetailsViewSelected = this._onDetailsViewSelected.bind(this);
     this._onPrefChanged = this._onPrefChanged.bind(this);
 
-    PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE, this._onRecordingStoppedOrSelected);
-    PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
+    PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE,
+                             this._onRecordingStoppedOrSelected);
+    PerformanceController.on(EVENTS.RECORDING_SELECTED,
+                             this._onRecordingStoppedOrSelected);
     PerformanceController.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
     OverviewView.on(EVENTS.UI_OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
     DetailsView.on(EVENTS.UI_DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
 
     let self = this;
     let originalRenderFn = this.render;
-    let afterRenderFn = () => this._wasRendered = true;
+    let afterRenderFn = () => {
+      this._wasRendered = true;
+    };
 
     this.render = Task.async(function* (...args) {
       let maybeRetval = yield originalRenderFn.apply(self, args);
       afterRenderFn();
       return maybeRetval;
     });
   },
 
   /**
    * Unbinds events.
    */
   destroy: function () {
     clearNamedTimeout("range-change-debounce");
 
-    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE, this._onRecordingStoppedOrSelected);
-    PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
+    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE,
+                              this._onRecordingStoppedOrSelected);
+    PerformanceController.off(EVENTS.RECORDING_SELECTED,
+                              this._onRecordingStoppedOrSelected);
     PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
     OverviewView.off(EVENTS.UI_OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
     DetailsView.off(EVENTS.UI_DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
   },
 
   /**
    * Returns true if this view was rendered at least once.
    */
@@ -128,17 +135,18 @@ var DetailsSubview = {
   _onOverviewRangeChange: function (_, interval) {
     if (!this.requiresUpdateOnRangeChange) {
       return;
     }
     if (DetailsView.isViewSelected(this)) {
       let debounced = () => {
         if (!this.shouldUpdateWhileMouseIsActive && OverviewView.isMouseActive) {
           // Don't render yet, while the selection is still being dragged.
-          setNamedTimeout("range-change-debounce", this.rangeChangeDebounceTime, debounced);
+          setNamedTimeout("range-change-debounce", this.rangeChangeDebounceTime,
+                          debounced);
         } else {
           this.render(interval);
         }
       };
       setNamedTimeout("range-change-debounce", this.rangeChangeDebounceTime, debounced);
     } else {
       this.shouldUpdateWhenShown = true;
     }
--- a/devtools/client/performance/views/details-js-call-tree.js
+++ b/devtools/client/performance/views/details-js-call-tree.js
@@ -1,28 +1,30 @@
 /* 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/. */
 /* import-globals-from ../performance-controller.js */
 /* import-globals-from ../performance-view.js */
+/* globals DetailsSubview */
 "use strict";
 
 /**
  * CallTree view containing profiler call tree, controlled by DetailsView.
  */
 var JsCallTreeView = Heritage.extend(DetailsSubview, {
 
   rerenderPrefs: [
     "invert-call-tree",
     "show-platform-data",
     "flatten-tree-recursion",
     "show-jit-optimizations",
   ],
 
-  rangeChangeDebounceTime: 75, // ms
+  // Units are in milliseconds.
+  rangeChangeDebounceTime: 75,
 
   /**
    * Sets up the view with event binding.
    */
   initialize: function () {
     DetailsSubview.initialize.call(this);
 
     this._onLink = this._onLink.bind(this);
@@ -76,17 +78,16 @@ var JsCallTreeView = Heritage.extend(Det
   },
 
   hideOptimizations: function () {
     this.optimizationsElement.classList.add("hidden");
   },
 
   _onFocus: function (_, treeItem) {
     let showOptimizations = PerformanceController.getOption("show-jit-optimizations");
-    let recording = PerformanceController.getCurrentRecording();
     let frameNode = treeItem.frame;
     let optimizationSites = frameNode && frameNode.hasOptimizations()
                             ? frameNode.getOptimizations().optimizationSites
                             : [];
 
     if (!showOptimizations || !frameNode || optimizationSites.length === 0) {
       this.hideOptimizations();
       this.emit("focus", treeItem);
@@ -131,17 +132,18 @@ var JsCallTreeView = Heritage.extend(Det
 
   /**
    * Called when the recording is stopped and prepares data to
    * populate the call tree.
    */
   _prepareCallTree: function (profile, { startTime, endTime }, options) {
     let thread = profile.threads[0];
     let { contentOnly, invertTree, flattenRecursion } = options;
-    let threadNode = new ThreadNode(thread, { startTime, endTime, contentOnly, invertTree, flattenRecursion });
+    let threadNode = new ThreadNode(thread, { startTime, endTime, contentOnly, invertTree,
+                                              flattenRecursion });
 
     // Real profiles from nsProfiler (i.e. not synthesized from allocation
     // logs) always have a (root) node. Go down one level in the uninverted
     // view to avoid displaying both the synthesized root node and the (root)
     // node from the profiler.
     if (!invertTree) {
       threadNode.calls = threadNode.calls[0].calls;
     }
--- a/devtools/client/performance/views/details-js-flamegraph.js
+++ b/devtools/client/performance/views/details-js-flamegraph.js
@@ -1,13 +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/. */
 /* import-globals-from ../performance-controller.js */
 /* import-globals-from ../performance-view.js */
+/* globals DetailsSubview */
 "use strict";
 
 /**
  * FlameGraph view containing a pyramid-like visualization of a profile,
  * controlled by DetailsView.
  */
 var JsFlameGraphView = Heritage.extend(DetailsSubview, {
 
@@ -61,17 +62,18 @@ var JsFlameGraphView = Heritage.extend(D
     let duration = recording.getDuration();
     let profile = recording.getProfile();
     let thread = profile.threads[0];
 
     let data = FlameGraphUtils.createFlameGraphDataFromThread(thread, {
       invertTree: PerformanceController.getOption("invert-flame-graph"),
       flattenRecursion: PerformanceController.getOption("flatten-tree-recursion"),
       contentOnly: !PerformanceController.getOption("show-platform-data"),
-      showIdleBlocks: PerformanceController.getOption("show-idle-blocks") && L10N.getStr("table.idle")
+      showIdleBlocks: PerformanceController.getOption("show-idle-blocks")
+                      && L10N.getStr("table.idle")
     });
 
     this.graph.setData({ data,
       bounds: {
         startTime: 0,
         endTime: duration
       },
       visible: {
--- a/devtools/client/performance/views/details-memory-call-tree.js
+++ b/devtools/client/performance/views/details-memory-call-tree.js
@@ -1,25 +1,27 @@
 /* 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/. */
 /* import-globals-from ../performance-controller.js */
 /* import-globals-from ../performance-view.js */
+/* globals DetailsSubview */
 "use strict";
 
 /**
  * CallTree view containing memory allocation sites, controlled by DetailsView.
  */
 var MemoryCallTreeView = Heritage.extend(DetailsSubview, {
 
   rerenderPrefs: [
     "invert-call-tree"
   ],
 
-  rangeChangeDebounceTime: 100, // ms
+  // Units are in milliseconds.
+  rangeChangeDebounceTime: 100,
 
   /**
    * Sets up the view with event binding.
    */
   initialize: function () {
     DetailsSubview.initialize.call(this);
 
     this._onLink = this._onLink.bind(this);
--- a/devtools/client/performance/views/details-memory-flamegraph.js
+++ b/devtools/client/performance/views/details-memory-flamegraph.js
@@ -1,13 +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/. */
 /* import-globals-from ../performance-controller.js */
 /* import-globals-from ../performance-view.js */
+/* globals DetailsSubview */
 "use strict";
 
 /**
  * FlameGraph view containing a pyramid-like visualization of memory allocation
  * sites, controlled by DetailsView.
  */
 var MemoryFlameGraphView = Heritage.extend(DetailsSubview, {
 
@@ -59,17 +60,18 @@ var MemoryFlameGraphView = Heritage.exte
     let recording = PerformanceController.getCurrentRecording();
     let duration = recording.getDuration();
     let allocations = recording.getAllocations();
 
     let thread = RecordingUtils.getProfileThreadFromAllocations(allocations);
     let data = FlameGraphUtils.createFlameGraphDataFromThread(thread, {
       invertStack: PerformanceController.getOption("invert-flame-graph"),
       flattenRecursion: PerformanceController.getOption("flatten-tree-recursion"),
-      showIdleBlocks: PerformanceController.getOption("show-idle-blocks") && L10N.getStr("table.idle")
+      showIdleBlocks: PerformanceController.getOption("show-idle-blocks")
+                      && L10N.getStr("table.idle")
     });
 
     this.graph.setData({ data,
       bounds: {
         startTime: 0,
         endTime: duration
       },
       visible: {
--- a/devtools/client/performance/views/details-waterfall.js
+++ b/devtools/client/performance/views/details-waterfall.js
@@ -1,18 +1,19 @@
 /* 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/. */
 /* import-globals-from ../performance-controller.js */
 /* import-globals-from ../performance-view.js */
-/* globals window */
+/* globals window, DetailsSubview */
 "use strict";
 
-const WATERFALL_RESIZE_EVENTS_DRAIN = 100; // ms
 const MARKER_DETAILS_WIDTH = 200;
+// Units are in milliseconds.
+const WATERFALL_RESIZE_EVENTS_DRAIN = 100;
 
 /**
  * Waterfall view containing the timeline markers, controlled by DetailsView.
  */
 var WaterfallView = Heritage.extend(DetailsSubview, {
 
   // Smallest unit of time between two markers. Larger by 10x^3 than Number.EPSILON.
   MARKER_EPSILON: 0.000000000001,
@@ -20,17 +21,18 @@ var WaterfallView = Heritage.extend(Deta
   observedPrefs: [
     "hidden-markers"
   ],
 
   rerenderPrefs: [
     "hidden-markers"
   ],
 
-  rangeChangeDebounceTime: 75, // ms
+  // Units are in milliseconds.
+  rangeChangeDebounceTime: 75,
 
   /**
    * Sets up the view with event binding.
    */
   initialize: function () {
     DetailsSubview.initialize.call(this);
 
     this._cache = new WeakMap();
@@ -41,17 +43,18 @@ var WaterfallView = Heritage.extend(Deta
     this._onShowAllocations = this._onShowAllocations.bind(this);
     this._hiddenMarkers = PerformanceController.getPref("hidden-markers");
 
     this.headerContainer = $("#waterfall-header");
     this.breakdownContainer = $("#waterfall-breakdown");
     this.detailsContainer = $("#waterfall-details");
     this.detailsSplitter = $("#waterfall-view > splitter");
 
-    this.details = new MarkerDetails($("#waterfall-details"), $("#waterfall-view > splitter"));
+    this.details = new MarkerDetails($("#waterfall-details"),
+                                     $("#waterfall-view > splitter"));
     this.details.hidden = true;
 
     this.details.on("resize", this._onResize);
     this.details.on("view-source", this._onViewSource);
     this.details.on("show-allocations", this._onShowAllocations);
     window.addEventListener("resize", this._onResize);
 
     // TODO bug 1167093 save the previously set width, and ensure minimum width
--- a/devtools/client/performance/views/details.js
+++ b/devtools/client/performance/views/details.js
@@ -1,13 +1,15 @@
 /* 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/. */
 /* import-globals-from ../performance-controller.js */
 /* import-globals-from ../performance-view.js */
+/* globals WaterfallView, JsCallTreeView, JsFlameGraphView, MemoryCallTreeView,
+           MemoryFlameGraphView, Iterator */
 "use strict";
 
 /**
  * Details view containing call trees, flamegraphs and markers waterfall.
  * Manages subviews and toggles visibility between them.
  */
 var DetailsView = {
   /**
@@ -53,42 +55,47 @@ var DetailsView = {
     this.setAvailableViews = this.setAvailableViews.bind(this);
 
     for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
       button.addEventListener("command", this._onViewToggle);
     }
 
     yield this.setAvailableViews();
 
-    PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE, this._onRecordingStoppedOrSelected);
-    PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
+    PerformanceController.on(EVENTS.RECORDING_STATE_CHANGE,
+                             this._onRecordingStoppedOrSelected);
+    PerformanceController.on(EVENTS.RECORDING_SELECTED,
+                             this._onRecordingStoppedOrSelected);
     PerformanceController.on(EVENTS.PREF_CHANGED, this.setAvailableViews);
   }),
 
   /**
    * Unbinds events, destroys subviews.
    */
   destroy: Task.async(function* () {
     for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
       button.removeEventListener("command", this._onViewToggle);
     }
 
-    for (let [_, component] of Iterator(this.components)) {
+    for (let [, component] of Iterator(this.components)) {
       component.initialized && (yield component.view.destroy());
     }
 
-    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE, this._onRecordingStoppedOrSelected);
-    PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
+    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE,
+                              this._onRecordingStoppedOrSelected);
+    PerformanceController.off(EVENTS.RECORDING_SELECTED,
+                              this._onRecordingStoppedOrSelected);
     PerformanceController.off(EVENTS.PREF_CHANGED, this.setAvailableViews);
   }),
 
   /**
    * Sets the possible views based off of recording features and server actor support
    * by hiding/showing the buttons that select them and going to default view
-   * if currently selected. Called when a preference changes in `devtools.performance.ui.`.
+   * if currently selected. Called when a preference changes in
+   * `devtools.performance.ui.`.
    */
   setAvailableViews: Task.async(function* () {
     let recording = PerformanceController.getCurrentRecording();
     let isCompleted = recording && recording.isCompleted();
     let invalidCurrentView = false;
 
     for (let [name, { view }] of Iterator(this.components)) {
       let isSupported = this._isViewSupported(name);
@@ -170,21 +177,20 @@ var DetailsView = {
    * and preferences enabled.
    */
   selectDefaultView: function () {
     // We want the waterfall to be default view in almost all cases, except when
     // timeline actor isn't supported, or we have markers disabled (which should only
     // occur temporarily via bug 1156499
     if (this._isViewSupported("waterfall")) {
       return this.selectView("waterfall");
-    } else {
-      // The JS CallTree should always be supported since the profiler
-      // actor is as old as the world.
-      return this.selectView("js-calltree");
     }
+    // The JS CallTree should always be supported since the profiler
+    // actor is as old as the world.
+    return this.selectView("js-calltree");
   },
 
   /**
    * Checks if the provided view is currently selected.
    *
    * @param object viewObject
    * @return boolean
    */
--- a/devtools/client/performance/views/overview.js
+++ b/devtools/client/performance/views/overview.js
@@ -1,21 +1,24 @@
 /* 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/. */
 /* import-globals-from ../performance-controller.js */
 /* import-globals-from ../performance-view.js */
+/* globals Iterator */
 "use strict";
 
 // No sense updating the overview more often than receiving data from the
 // backend. Make sure this isn't lower than DEFAULT_TIMELINE_DATA_PULL_TIMEOUT
 // in devtools/server/actors/timeline.js
-const OVERVIEW_UPDATE_INTERVAL = 200; // ms
-const FRAMERATE_GRAPH_LOW_RES_INTERVAL = 100; // ms
-const FRAMERATE_GRAPH_HIGH_RES_INTERVAL = 16; // ms
+
+// The following units are in milliseconds.
+const OVERVIEW_UPDATE_INTERVAL = 200;
+const FRAMERATE_GRAPH_LOW_RES_INTERVAL = 100;
+const FRAMERATE_GRAPH_HIGH_RES_INTERVAL = 16;
 const GRAPH_REQUIREMENTS = {
   timeline: {
     features: ["withMarkers"]
   },
   framerate: {
     features: ["withTicks"]
   },
   memory: {
@@ -73,17 +76,18 @@ var OverviewView = {
   },
 
   /**
    * Unbinds events.
    */
   destroy: Task.async(function* () {
     PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
     PerformanceController.off(EVENTS.THEME_CHANGED, this._onThemeChanged);
-    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE, this._onRecordingStateChange);
+    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE,
+                              this._onRecordingStateChange);
     PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
     this.graphs.off("selecting", this._onGraphSelecting);
     this.graphs.off("rendered", this._onGraphRendered);
     yield this.graphs.destroy();
   }),
 
   /**
    * Returns true if any of the overview graphs have mouse dragging active,
@@ -201,31 +205,32 @@ var OverviewView = {
     if (this.isRendering()) {
       this._timeoutId = setTimeout(this._onRecordingTick, this.OVERVIEW_UPDATE_INTERVAL);
     }
   },
 
   /**
    * Called when recording state changes.
    */
-  _onRecordingStateChange: OverviewViewOnStateChange(Task.async(function* (_, state, recording) {
-    if (state !== "recording-stopped") {
-      return;
-    }
-    // Check to see if the recording that just stopped is the current recording.
-    // If it is, render the high-res graphs. For manual recordings, it will also
-    // be the current recording, but profiles generated by `console.profile` can stop
-    // while having another profile selected -- in this case, OverviewView should keep
-    // rendering the current recording.
-    if (recording !== PerformanceController.getCurrentRecording()) {
-      return;
-    }
-    this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
-    yield this._checkSelection(recording);
-  })),
+  _onRecordingStateChange: OverviewViewOnStateChange(Task.async(
+    function* (_, state, recording) {
+      if (state !== "recording-stopped") {
+        return;
+      }
+      // Check to see if the recording that just stopped is the current recording.
+      // If it is, render the high-res graphs. For manual recordings, it will also
+      // be the current recording, but profiles generated by `console.profile` can stop
+      // while having another profile selected -- in this case, OverviewView should keep
+      // rendering the current recording.
+      if (recording !== PerformanceController.getCurrentRecording()) {
+        return;
+      }
+      this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
+      yield this._checkSelection(recording);
+    })),
 
   /**
    * Called when a new recording is selected.
    */
   _onRecordingSelected: OverviewViewOnStateChange(Task.async(function* (_, recording) {
     this._setGraphVisibilityFromRecordingFeatures(recording);
 
     // If this recording is complete, render the high res graph
@@ -297,30 +302,31 @@ var OverviewView = {
    * Called whenever a preference in `devtools.performance.ui.` changes.
    * Does not care about the enabling of memory/framerate graphs,
    * because those will set values on a recording model, and
    * the graphs will render based on the existence.
    */
   _onPrefChanged: Task.async(function* (_, prefName, prefValue) {
     switch (prefName) {
       case "hidden-markers": {
-        let graph;
-        if (graph = yield this.graphs.isAvailable("timeline")) {
+        let graph = yield this.graphs.isAvailable("timeline");
+        if (graph) {
           let filter = PerformanceController.getPref("hidden-markers");
           graph.setFilter(filter);
           graph.refresh({ force: true });
         }
         break;
       }
     }
   }),
 
   _setGraphVisibilityFromRecordingFeatures: function (recording) {
     for (let [graphName, requirements] of Iterator(GRAPH_REQUIREMENTS)) {
-      this.graphs.enable(graphName, PerformanceController.isFeatureSupported(requirements.features));
+      this.graphs.enable(graphName,
+                         PerformanceController.isFeatureSupported(requirements.features));
     }
   },
 
   /**
    * Fetch the multiprocess status and if e10s is not currently on, disable
    * realtime rendering.
    *
    * @return {boolean}
@@ -393,19 +399,18 @@ function OverviewViewOnStateChange(fn) {
 
     // If realtime rendering is not enabed (e10s not on), then
     // show the disabled message, or the full graphs if the recording is completed
     if (!this.isRealtimeRenderingEnabled()) {
       if (recording.isRecording()) {
         this._hideGraphsPanel();
         // Abort, as we do not want to change polling status.
         return;
-      } else {
-        this._showGraphsPanel(recording);
       }
+      this._showGraphsPanel(recording);
     }
 
     if (this.isRendering() && !currentRecording.isRecording()) {
       this._stopPolling();
     } else if (currentRecording.isRecording() && !this.isRendering()) {
       this._startPolling();
     }
 
--- a/devtools/client/performance/views/recordings.js
+++ b/devtools/client/performance/views/recordings.js
@@ -31,17 +31,18 @@ var RecordingsView = Heritage.extend(Wid
     PerformanceController.on(EVENTS.RECORDING_EXPORTED, this._onRecordingExported);
     this.widget.addEventListener("select", this._onSelect, false);
   },
 
   /**
    * Destruction function, called when the tool is closed.
    */
   destroy: function () {
-    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE, this._onRecordingStateChange);
+    PerformanceController.off(EVENTS.RECORDING_STATE_CHANGE,
+                              this._onRecordingStateChange);
     PerformanceController.off(EVENTS.RECORDING_ADDED, this._onNewRecording);
     PerformanceController.off(EVENTS.RECORDING_DELETED, this._onRecordingDeleted);
     PerformanceController.off(EVENTS.RECORDING_EXPORTED, this._onRecordingExported);
     this.widget.removeEventListener("select", this._onSelect, false);
   },
 
   /**
    * Adds an empty recording to this container.
@@ -186,17 +187,18 @@ var RecordingsView = Heritage.extend(Wid
     this.emit(EVENTS.UI_RECORDING_SELECTED, model);
   }),
 
   /**
    * The click listener for the "save" button of each item in this container.
    */
   _onSaveButtonClick: function (e) {
     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
-    fp.init(window, L10N.getStr("recordingsList.saveDialogTitle"), Ci.nsIFilePicker.modeSave);
+    fp.init(window, L10N.getStr("recordingsList.saveDialogTitle"),
+            Ci.nsIFilePicker.modeSave);
     fp.appendFilter(L10N.getStr("recordingsList.saveDialogJSONFilter"), "*.json");
     fp.appendFilter(L10N.getStr("recordingsList.saveDialogAllFilter"), "*.*");
     fp.defaultString = "profile.json";
 
     fp.open({ done: result => {
       if (result == Ci.nsIFilePicker.returnCancel) {
         return;
       }
--- a/devtools/client/performance/views/toolbar.js
+++ b/devtools/client/performance/views/toolbar.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/. */
 /* import-globals-from ../performance-controller.js */
 /* import-globals-from ../performance-view.js */
-/* globals document */
+/* globals document, Iterator */
 "use strict";
 
 /**
  * View handler for toolbar events (mostly option toggling and triggering)
  */
 var ToolbarView = {
   /**
    * Sets up the view with event binding.
@@ -30,43 +30,48 @@ var ToolbarView = {
     let experimentalEnabled = PerformanceController.getOption("experimental");
     this._toggleExperimentalUI(experimentalEnabled);
 
     yield this.optionsView.initialize();
     this.optionsView.on("pref-changed", this._onPrefChanged);
 
     this._buildMarkersFilterPopup();
     this._updateHiddenMarkersPopup();
-    $("#performance-filter-menupopup").addEventListener("popupshowing", this._onFilterPopupShowing);
-    $("#performance-filter-menupopup").addEventListener("popuphiding", this._onFilterPopupHiding);
+    $("#performance-filter-menupopup").addEventListener("popupshowing",
+                                                        this._onFilterPopupShowing);
+    $("#performance-filter-menupopup").addEventListener("popuphiding",
+                                                        this._onFilterPopupHiding);
   }),
 
   /**
    * Unbinds events and cleans up view.
    */
   destroy: function () {
-    $("#performance-filter-menupopup").removeEventListener("popupshowing", this._onFilterPopupShowing);
-    $("#performance-filter-menupopup").removeEventListener("popuphiding", this._onFilterPopupHiding);
+    $("#performance-filter-menupopup").removeEventListener("popupshowing",
+                                                           this._onFilterPopupShowing);
+    $("#performance-filter-menupopup").removeEventListener("popuphiding",
+                                                           this._onFilterPopupHiding);
     this._popup = null;
 
     this.optionsView.off("pref-changed", this._onPrefChanged);
     this.optionsView.destroy();
   },
 
   /**
    * Creates the timeline markers filter popup.
    */
   _buildMarkersFilterPopup: function () {
     for (let [markerName, markerDetails] of Iterator(TIMELINE_BLUEPRINT)) {
       let menuitem = document.createElement("menuitem");
       menuitem.setAttribute("closemenu", "none");
       menuitem.setAttribute("type", "checkbox");
       menuitem.setAttribute("align", "center");
       menuitem.setAttribute("flex", "1");
-      menuitem.setAttribute("label", MarkerBlueprintUtils.getMarkerGenericName(markerName));
+      menuitem.setAttribute("label",
+                            MarkerBlueprintUtils.getMarkerGenericName(markerName));
       menuitem.setAttribute("marker-type", markerName);
       menuitem.className = `marker-color-${markerDetails.colorName}`;
 
       menuitem.addEventListener("command", this._onHiddenMarkersChanged);
 
       $("#performance-filter-menupopup").appendChild(menuitem);
     }
   },
@@ -91,17 +96,18 @@ var ToolbarView = {
    * Fired when `devtools.performance.ui.experimental` is changed, or
    * during init. Toggles the visibility of experimental performance tool options
    * in the UI options.
    *
    * Sets or removes "experimental-enabled" on the menu and main elements,
    * hiding or showing all elements with class "experimental-option".
    *
    * TODO re-enable "#option-enable-memory" permanently once stable in bug 1163350
-   * TODO re-enable "#option-show-jit-optimizations" permanently once stable in bug 1163351
+   * TODO re-enable "#option-show-jit-optimizations" permanently once stable in
+   *      bug 1163351
    *
    * @param {boolean} isEnabled
    */
   _toggleExperimentalUI: function (isEnabled) {
     if (isEnabled) {
       $(".theme-body").classList.add("experimental-enabled");
       this._popup.classList.add("experimental-enabled");
     } else {
@@ -123,17 +129,18 @@ var ToolbarView = {
   _onFilterPopupHiding: function () {
     $("#filter-button").removeAttribute("open");
   },
 
   /**
    * Fired when a menu item in the markers filter popup is checked or unchecked.
    */
   _onHiddenMarkersChanged: function () {
-    let checkedMenuItems = $$("#performance-filter-menupopup menuitem[marker-type]:not([checked])");
+    let checkedMenuItems =
+      $$("#performance-filter-menupopup menuitem[marker-type]:not([checked])");
     let hiddenMarkers = Array.map(checkedMenuItems, e => e.getAttribute("marker-type"));
     PerformanceController.setPref("hidden-markers", hiddenMarkers);
   },
 
   /**
    * Fired when a preference changes in the underlying OptionsView.
    * Propogated by the PerformanceController.
    */
--- a/devtools/client/shared/components/reps/array.js
+++ b/devtools/client/shared/components/reps/array.js
@@ -27,17 +27,17 @@ define(function (require, exports, modul
     getTitle: function (object, context) {
       return "[" + object.length + "]";
     },
 
     arrayIterator: function (array, max) {
       let items = [];
       let delim;
 
-      for (let i = 0; i < array.length && i <= max; i++) {
+      for (let i = 0; i < array.length && i < max; i++) {
         try {
           let value = array[i];
 
           delim = (i == array.length - 1 ? "" : ", ");
 
           if (value === array) {
             items.push(Reference({
               key: i,
@@ -56,24 +56,22 @@ define(function (require, exports, modul
             object: exc,
             delim: delim,
             key: i
           }));
         }
       }
 
       if (array.length > max) {
-        items.pop();
-
         let objectLink = this.props.objectLink || DOM.span;
         items.push(Caption({
           key: "more",
           object: objectLink({
             object: this.props.object
-          }, "more…")
+          }, (array.length - max) + " more…")
         }));
       }
 
       return items;
     },
 
     /**
      * Returns true if the passed object is an array with additional (custom)
--- a/devtools/client/shared/components/reps/grip-array.js
+++ b/devtools/client/shared/components/reps/grip-array.js
@@ -54,17 +54,17 @@ define(function (require, exports, modul
       let array = grip.preview.items;
       if (!array) {
         return items;
       }
 
       let delim;
       let provider = this.props.provider;
 
-      for (let i = 0; i < array.length && i <= max; i++) {
+      for (let i = 0; i < array.length && i < max; i++) {
         try {
           let itemGrip = array[i];
           let value = provider ? provider.getValue(itemGrip) : itemGrip;
 
           delim = (i == array.length - 1 ? "" : ", ");
 
           if (value === array) {
             items.push(Reference({
@@ -84,23 +84,22 @@ define(function (require, exports, modul
             object: exc,
             delim: delim,
             key: i}
           )));
         }
       }
 
       if (array.length > max) {
-        items.pop();
         let objectLink = this.props.objectLink || span;
         items.push(Caption({
           key: "more",
           object: objectLink({
             object: this.props.object
-          }, "more…")
+          }, (grip.preview.length - max) + " more…")
         }));
       }
 
       return items;
     },
 
     render: function () {
       let mode = this.props.mode || "short";
--- a/devtools/client/shared/components/reps/grip.js
+++ b/devtools/client/shared/components/reps/grip.js
@@ -74,17 +74,17 @@ define(function (require, exports, modul
       if (props.length < object.ownPropertyLength) {
         // There are some undisplayed props. Then display "more...".
         let objectLink = this.props.objectLink || span;
 
         props.push(Caption({
           key: "more",
           object: objectLink({
             object: object
-          }, "more…")
+          }, ((object ? object.ownPropertyLength : 0) - max) + " more…")
         }));
       } else if (props.length > 0) {
         // Remove the last comma.
         // NOTE: do not change comp._store.props directly to update a property,
         // it should be re-rendered or cloned with changed props
         let last = props.length - 1;
         props[last] = React.cloneElement(props[last], {
           delim: ""
--- a/devtools/client/shared/components/reps/object.js
+++ b/devtools/client/shared/components/reps/object.js
@@ -73,17 +73,17 @@ define(function (require, exports, modul
       if (props.length > max) {
         props.pop();
         let objectLink = this.props.objectLink || span;
 
         props.push(Caption({
           key: "more",
           object: objectLink({
             object: object
-          }, "more…")
+          }, (Object.keys(object).length - max) + " more…")
         }));
       } else if (props.length > 0) {
         // Remove the last comma.
         props[props.length - 1] = React.cloneElement(
           props[props.length - 1], { delim: "" });
       }
 
       return props;
--- a/devtools/client/shared/components/test/mochitest/test_reps_array.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_array.html
@@ -102,17 +102,17 @@ window.onload = Task.async(function* () 
       }
     ];
 
     testRepRenderModes(modeTests, "testMaxProps", componentUnderTest, stub);
   }
 
   function testMoreThanShortMaxProps() {
     const stub = Array(maxLength.short + 1).fill("foo");
-    const defaultShortOutput = `[${Array(maxLength.short).fill("\"foo\"").join(", ")}, more…]`;
+    const defaultShortOutput = `[${Array(maxLength.short).fill("\"foo\"").join(", ")}, 1 more…]`;
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultShortOutput,
       },
       {
         mode: "tiny",
@@ -128,18 +128,18 @@ window.onload = Task.async(function* () 
       }
     ];
 
     testRepRenderModes(modeTests, "testMoreThanMaxProps", componentUnderTest, stub);
   }
 
   function testMoreThanLongMaxProps() {
     const stub = Array(maxLength.long + 1).fill("foo");
-    const defaultShortOutput = `[${Array(maxLength.short).fill("\"foo\"").join(", ")}, more…]`;
-    const defaultLongOutput = `[${Array(maxLength.long).fill("\"foo\"").join(", ")}, more…]`;
+    const defaultShortOutput = `[${Array(maxLength.short).fill("\"foo\"").join(", ")}, ${maxLength.long + 1 - maxLength.short} more…]`;
+    const defaultLongOutput = `[${Array(maxLength.long).fill("\"foo\"").join(", ")}, 1 more…]`;
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultShortOutput,
       },
       {
         mode: "tiny",
@@ -189,17 +189,17 @@ window.onload = Task.async(function* () 
     let stub = [
       {
         p1: "s1",
         p2: ["a1", "a2", "a3"],
         p3: "s3",
         p4: "s4"
       }
     ];
-    const defaultOutput = `[Object{p1: "s1", p3: "s3", p4: "s4", more…}]`;
+    const defaultOutput = `[Object{p1: "s1", p3: "s3", p4: "s4", 1 more…}]`;
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       },
       {
         mode: "tiny",
--- a/devtools/client/shared/components/test/mochitest/test_reps_grip-array.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_grip-array.html
@@ -101,17 +101,17 @@ window.onload = Task.async(function* () 
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
   function testMoreThanShortMaxProps() {
     // Test array = `["test string"…] //4 items`
     const testName = "testMoreThanShortMaxProps";
 
-    const defaultOutput = `Array[${Array(maxLength.short).fill("\"test string\"").join(", ")}, more…]`;
+    const defaultOutput = `Array[${Array(maxLength.short).fill("\"test string\"").join(", ")}, 1 more…]`;
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       },
       {
         mode: "tiny",
@@ -129,18 +129,18 @@ window.onload = Task.async(function* () 
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
   function testMoreThanLongMaxProps() {
     // Test array = `["test string"…] //301 items`
     const testName = "testMoreThanLongMaxProps";
 
-    const defaultShortOutput = `Array[${Array(maxLength.short).fill("\"test string\"").join(", ")}, more…]`;
-    const defaultLongOutput = `Array[${Array(maxLength.long).fill("\"test string\"").join(", ")}, more…]`;
+    const defaultShortOutput = `Array[${Array(maxLength.short).fill("\"test string\"").join(", ")}, ${maxLength.long + 1 - maxLength.short} more…]`;
+    const defaultLongOutput = `Array[${Array(maxLength.long).fill("\"test string\"").join(", ")}, 1 more…]`;
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultShortOutput,
       },
       {
         mode: "tiny",
--- a/devtools/client/shared/components/test/mochitest/test_reps_grip.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_grip.html
@@ -95,29 +95,29 @@ window.onload = Task.async(function* () 
         expectedOutput: defaultOutput,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
   function testMoreThanMaxProps() {
-    // Test object = `{p0: "0", p1: "1", p2: "2", …, p101: "101"}`
+    // Test object = `{p0: "0", p1: "1", p2: "2", …, p100: "100"}`
     const testName = "testMoreThanMaxProps";
 
-    const defaultOutput = `Object {p0: "0", p1: "1", p2: "2", more…}`;
+    const defaultOutput = `Object {p0: "0", p1: "1", p2: "2", 98 more…}`;
 
     // Generate string with 100 properties, which is the max limit
     // for 'long' mode.
     let props = "";
     for (let i = 0; i < 100; i++) {
       props += "p" + i + ": \"" + i + "\", ";
     }
 
-    const longOutput = `Object {${props}more…}`;
+    const longOutput = `Object {${props}1 more…}`;
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       },
       {
         mode: "tiny",
@@ -135,17 +135,17 @@ window.onload = Task.async(function* () 
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
   function testUninterestingProps() {
     // Test object: `{a: undefined, b: undefined, c: "c", d: 1}`
     // @TODO This is not how we actually want the preview to be output.
     // See https://bugzilla.mozilla.org/show_bug.cgi?id=1276376
-    const expectedOutput = `Object {a: undefined, b: undefined, c: "c", more…}`;
+    const expectedOutput = `Object {a: undefined, b: undefined, c: "c", 1 more…}`;
   }
 
   function testNestedObject() {
     // Test object: `{objProp: {id: 1}, strProp: "test string"}`
     const testName = "testNestedObject";
 
     const defaultOutput = `Object {objProp: Object, strProp: "test string"}`;
 
--- a/devtools/client/shared/components/test/mochitest/test_reps_object.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_object.html
@@ -96,17 +96,17 @@ window.onload = Task.async(function* () 
     testRepRenderModes(modeTests, "testMaxProps", componentUnderTest, stub);
   }
 
   function testMoreThanMaxProps() {
     let stub = {};
     for (let i = 0; i<100; i++) {
       stub[`p${i}`] = i
     }
-    const defaultOutput = `Object{p0: 0, p1: 1, p2: 2, more…}`;
+    const defaultOutput = `Object{p0: 0, p1: 1, p2: 2, 97 more…}`;
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       },
       {
         mode: "tiny",
@@ -122,17 +122,17 @@ window.onload = Task.async(function* () 
       }
     ];
 
     testRepRenderModes(modeTests, "testMoreThanMaxProps", componentUnderTest, stub);
   }
 
   function testUninterestingProps() {
     const stub = {a:undefined, b:undefined, c:"c", d:0};
-    const defaultOutput = `Object{c: "c", d: 0, a: undefined, more…}`;
+    const defaultOutput = `Object{c: "c", d: 0, a: undefined, 1 more…}`;
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       },
       {
         mode: "tiny",
--- a/devtools/client/themes/images/add.svg
+++ b/devtools/client/themes/images/add.svg
@@ -1,6 +1,6 @@
 <!-- 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/. -->
 <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#0b0b0b">
-  <path d="M8.5 8.5V14a.5.5 0 1 1-1 0V8.5H2a.5.5 0 0 1 0-1h5.5V2a.5.5 0 0 1 1 0v5.5H14a.5.5 0 1 1 0 1H8.5z"/>
+  <path d="M8 8v5.5c0 .276-.224.5-.5.5s-.5-.224-.5-.5V8H1.5c-.276 0-.5-.224-.5-.5s.224-.5.5-.5H7V1.5c0-.276.224-.5.5-.5s.5.224.5.5V7h5.5c.276 0 .5.224.5.5s-.224.5-.5.5H8z"/>
 </svg>