Bug 1131577 - Waterfall view can scroll outside the visible bounds if the flamegraph view was initialized before a recording was started, r=jsantell
authorVictor Porof <vporof@mozilla.com>
Tue, 10 Feb 2015 18:36:31 -0500
changeset 242174 6692f4d0a461c515da6eee86d8d86f9799be5b87
parent 242173 7c4d19d34725ac259cadd9b5bcc02c6918c34491
child 242175 ea1ed091f31eb3dc92bcbb5d70d14a710c782346
push id649
push userwcosta@mozilla.com
push dateWed, 11 Feb 2015 16:57:44 +0000
reviewersjsantell
bugs1131577
milestone38.0a1
Bug 1131577 - Waterfall view can scroll outside the visible bounds if the flamegraph view was initialized before a recording was started, r=jsantell
browser/devtools/performance/test/browser.ini
browser/devtools/performance/test/browser_perf-details-03.js
browser/devtools/performance/test/browser_perf-details-04.js
browser/devtools/performance/test/browser_perf-details-memory-calltree-render.js
browser/devtools/performance/test/browser_perf-details-memory-flamegraph-render.js
browser/devtools/performance/test/head.js
browser/devtools/performance/views/details.js
--- a/browser/devtools/performance/test/browser.ini
+++ b/browser/devtools/performance/test/browser.ini
@@ -19,16 +19,17 @@ support-files =
 [browser_perf-details-calltree-render.js]
 [browser_perf-details-flamegraph-render.js]
 [browser_perf-details-memory-calltree-render.js]
 [browser_perf-details-memory-flamegraph-render.js]
 [browser_perf-details-waterfall-render.js]
 [browser_perf-details-01.js]
 [browser_perf-details-02.js]
 [browser_perf-details-03.js]
+[browser_perf-details-04.js]
 [browser_perf-events-calltree.js]
 [browser_perf-front-basic-profiler-01.js]
 [browser_perf-front-basic-timeline-01.js]
 #[browser_perf-front-profiler-01.js] bug 1077464
 [browser_perf-front-profiler-02.js]
 [browser_perf-front-profiler-03.js]
 [browser_perf-front-profiler-04.js]
 #[browser_perf-front-profiler-05.js] bug 1077464
--- a/browser/devtools/performance/test/browser_perf-details-03.js
+++ b/browser/devtools/performance/test/browser_perf-details-03.js
@@ -1,58 +1,84 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let MEMORY_PREF = "devtools.performance.ui.enable-memory";
-
 /**
  * Tests that the details view hides the memory buttons when `enable-memory` is toggled,
  * and that it switches to default panel if toggling while a memory panel is selected.
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
-  let { EVENTS, DetailsView } = panel.panelWin;
+  let { EVENTS, PerformanceController, OverviewView, DetailsView } = panel.panelWin;
   let { $, WaterfallView, MemoryCallTreeView, MemoryFlameGraphView } = panel.panelWin;
 
-  Services.prefs.setBoolPref(MEMORY_PREF, false);
-
   ok(DetailsView.isViewSelected(WaterfallView),
     "The waterfall view is selected by default in the details view.");
 
+  // The toolbar buttons will always be hidden when a recording isn't available,
+  // so make sure we have one that's finished.
+  yield startRecording(panel);
+  yield stopRecording(panel);
+
   let flameBtn = $("toolbarbutton[data-view='memory-flamegraph']");
   let callBtn = $("toolbarbutton[data-view='memory-calltree']");
 
+  Services.prefs.setBoolPref(MEMORY_PREF, false);
   is(flameBtn.hidden, true, "memory-flamegraph button hidden when enable-memory=false");
   is(callBtn.hidden, true, "memory-calltree button hidden when enable-memory=false");
 
   Services.prefs.setBoolPref(MEMORY_PREF, true);
-
   is(flameBtn.hidden, false, "memory-flamegraph button shown when enable-memory=true");
   is(callBtn.hidden, false, "memory-calltree button shown when enable-memory=true");
 
   let selected = DetailsView.whenViewSelected(MemoryCallTreeView);
   let notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
   DetailsView.selectView("memory-calltree");
   yield Promise.all([selected, notified]);
 
+  ok(DetailsView.isViewSelected(MemoryCallTreeView),
+    "The memory call tree view can now be selected.");
+
+  selected = DetailsView.whenViewSelected(MemoryFlameGraphView);
+  notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
+  DetailsView.selectView("memory-flamegraph");
+  yield Promise.all([selected, notified]);
+
+  ok(DetailsView.isViewSelected(MemoryFlameGraphView),
+    "The memory flamegraph view can now be selected.");
+
   selected = DetailsView.whenViewSelected(WaterfallView);
   notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
   Services.prefs.setBoolPref(MEMORY_PREF, false);
   yield Promise.all([selected, notified]);
 
   ok(DetailsView.isViewSelected(WaterfallView),
     "The waterfall view is now selected when toggling off enable-memory when a memory panel is selected.");
 
   Services.prefs.setBoolPref(MEMORY_PREF, true);
 
+  selected = DetailsView.whenViewSelected(MemoryCallTreeView);
+  notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
+  DetailsView.selectView("memory-calltree");
+  yield Promise.all([selected, notified]);
+
+  ok(DetailsView.isViewSelected(MemoryCallTreeView),
+    "The memory call tree view can be selected again after re-enabling memory.");
+
   selected = DetailsView.whenViewSelected(MemoryFlameGraphView);
   notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
   DetailsView.selectView("memory-flamegraph");
   yield Promise.all([selected, notified]);
 
+  ok(DetailsView.isViewSelected(MemoryFlameGraphView),
+    "The memory flamegraph view can be selected again after re-enabling memory.");
+
   selected = DetailsView.whenViewSelected(WaterfallView);
   notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
   Services.prefs.setBoolPref(MEMORY_PREF, false);
   yield Promise.all([selected, notified]);
 
+  ok(DetailsView.isViewSelected(WaterfallView),
+    "The waterfall view is now selected when toggling off enable-memory when a memory panel is selected.");
+
   yield teardown(panel);
   finish();
 }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-details-04.js
@@ -0,0 +1,87 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that the details view hides the toolbar buttons when a recording
+ * doesn't exist or is in progress.
+ */
+function spawnTest () {
+  let { panel } = yield initPerformance(SIMPLE_URL);
+  let { EVENTS, $, $$, PerformanceController, RecordingsView, DetailsView } = panel.panelWin;
+
+  let waterfallBtn = $("toolbarbutton[data-view='waterfall']");
+  let jsFlameBtn = $("toolbarbutton[data-view='js-flamegraph']");
+  let jsCallBtn = $("toolbarbutton[data-view='js-calltree']");
+  let memFlameBtn = $("toolbarbutton[data-view='memory-flamegraph']");
+  let memCallBtn = $("toolbarbutton[data-view='memory-calltree']");
+
+  is(waterfallBtn.hidden, true, "waterfall button hidden when tool starts.");
+  is(jsFlameBtn.hidden, true, "js-flamegraph button hidden when tool starts.");
+  is(jsCallBtn.hidden, true, "js-calltree button hidden when tool starts.");
+  is(memFlameBtn.hidden, true, "memory-flamegraph button hidden when tool starts.");
+  is(memCallBtn.hidden, true, "memory-calltree button hidden when tool starts.");
+
+  yield startRecording(panel);
+
+  is(waterfallBtn.hidden, true, "waterfall button hidden when recording starts.");
+  is(jsFlameBtn.hidden, true, "js-flamegraph button hidden when recording starts.");
+  is(jsCallBtn.hidden, true, "js-calltree button hidden when recording starts.");
+  is(memFlameBtn.hidden, true, "memory-flamegraph button hidden when recording starts.");
+  is(memCallBtn.hidden, true, "memory-calltree button hidden when recording starts.");
+
+  yield stopRecording(panel);
+
+  is(waterfallBtn.hidden, false, "waterfall button visible when recording ends.");
+  is(jsFlameBtn.hidden, false, "js-flamegraph button visible when recording ends.");
+  is(jsCallBtn.hidden, false, "js-calltree button visible when recording ends.");
+  is(memFlameBtn.hidden, true, "memory-flamegraph button hidden when recording ends.");
+  is(memCallBtn.hidden, true, "memory-calltree button hidden when recording ends.");
+
+  yield startRecording(panel);
+
+  is(waterfallBtn.hidden, true, "waterfall button hidden when another recording starts.");
+  is(jsFlameBtn.hidden, true, "js-flamegraph button hidden when another recording starts.");
+  is(jsCallBtn.hidden, true, "js-calltree button hidden when another recording starts.");
+  is(memFlameBtn.hidden, true, "memory-flamegraph button hidden when another recording starts.");
+  is(memCallBtn.hidden, true, "memory-calltree button hidden when another recording starts.");
+
+  let select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+  mousedown(panel.panelWin, $$(".recording-item")[0]);
+  yield select;
+
+  is(RecordingsView.selectedIndex, 0,
+    "The first recording was selected again.");
+
+  is(waterfallBtn.hidden, false, "waterfall button visible when first recording selected.");
+  is(jsFlameBtn.hidden, false, "js-flamegraph button visible when first recording selected.");
+  is(jsCallBtn.hidden, false, "js-calltree button visible when first recording selected.");
+  is(memFlameBtn.hidden, true, "memory-flamegraph button hidden when first recording selected.");
+  is(memCallBtn.hidden, true, "memory-calltree button hidden when first recording selected.");
+
+  select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
+  mousedown(panel.panelWin, $$(".recording-item")[1]);
+  yield select;
+
+  is(RecordingsView.selectedIndex, 1,
+    "The second recording was selected again.");
+
+  is(waterfallBtn.hidden, true, "waterfall button still hidden when second recording selected.");
+  is(jsFlameBtn.hidden, true, "js-flamegraph button still hidden when second recording selected.");
+  is(jsCallBtn.hidden, true, "js-calltree button still hidden when second recording selected.");
+  is(memFlameBtn.hidden, true, "memory-flamegraph button still hidden when second recording selected.");
+  is(memCallBtn.hidden, true, "memory-calltree button still hidden when second recording selected.");
+
+  yield stopRecording(panel);
+
+  is(RecordingsView.selectedIndex, 1,
+    "The second recording is still selected.");
+
+  is(waterfallBtn.hidden, false, "waterfall button visible when second recording finished.");
+  is(jsFlameBtn.hidden, false, "js-flamegraph button visible when second recording finished.");
+  is(jsCallBtn.hidden, false, "js-calltree button visible when second recording finished.");
+  is(memFlameBtn.hidden, true, "memory-flamegraph button hidden when second recording finished.");
+  is(memCallBtn.hidden, true, "memory-calltree button hidden when second recording finished.");
+
+  yield teardown(panel);
+  finish();
+}
--- a/browser/devtools/performance/test/browser_perf-details-memory-calltree-render.js
+++ b/browser/devtools/performance/test/browser_perf-details-memory-calltree-render.js
@@ -3,16 +3,19 @@
 
 /**
  * Tests that the memory call tree view renders content after recording.
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, MemoryCallTreeView } = panel.panelWin;
 
+  // Enable memory to test.
+  Services.prefs.setBoolPref(MEMORY_PREF, true);
+
   yield DetailsView.selectView("memory-calltree");
   ok(DetailsView.isViewSelected(MemoryCallTreeView), "The call tree is now selected.");
 
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(MemoryCallTreeView, EVENTS.MEMORY_CALL_TREE_RENDERED);
   yield stopRecording(panel);
--- a/browser/devtools/performance/test/browser_perf-details-memory-flamegraph-render.js
+++ b/browser/devtools/performance/test/browser_perf-details-memory-flamegraph-render.js
@@ -3,16 +3,19 @@
 
 /**
  * Tests that the memory flamegraph view renders content after recording.
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, MemoryFlameGraphView } = panel.panelWin;
 
+  // Enable memory to test.
+  Services.prefs.setBoolPref(MEMORY_PREF, true);
+
   yield DetailsView.selectView("memory-flamegraph");
   ok(DetailsView.isViewSelected(MemoryFlameGraphView), "The flamegraph is now selected.");
 
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(MemoryFlameGraphView, EVENTS.MEMORY_FLAMEGRAPH_RENDERED);
   yield stopRecording(panel);
--- a/browser/devtools/performance/test/head.js
+++ b/browser/devtools/performance/test/head.js
@@ -257,16 +257,20 @@ function command (button) {
   ev.initCommandEvent("command", true, true, button.ownerDocument.defaultView, 0, false, false, false, false, null);
   button.dispatchEvent(ev);
 }
 
 function click (win, button) {
   EventUtils.sendMouseEvent({ type: "click" }, button, win);
 }
 
+function mousedown (win, button) {
+  EventUtils.sendMouseEvent({ type: "mousedown" }, button, win);
+}
+
 function* startRecording(panel) {
   let win = panel.panelWin;
   let clicked = panel.panelWin.PerformanceView.once(win.EVENTS.UI_START_RECORDING);
   let willStart = panel.panelWin.PerformanceController.once(win.EVENTS.RECORDING_WILL_START);
   let hasStarted = panel.panelWin.PerformanceController.once(win.EVENTS.RECORDING_STARTED);
   let button = win.$("#main-record-button");
 
   ok(!button.hasAttribute("checked"),
--- a/browser/devtools/performance/views/details.js
+++ b/browser/devtools/performance/views/details.js
@@ -24,59 +24,64 @@ let DetailsView = {
   /**
    * Sets up the view with event binding, initializes subviews.
    */
   initialize: Task.async(function *() {
     this.el = $("#details-pane");
     this.toolbar = $("#performance-toolbar-controls-detail-views");
 
     this._onViewToggle = this._onViewToggle.bind(this);
+    this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
     this.setAvailableViews = this.setAvailableViews.bind(this);
 
     for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
       button.addEventListener("command", this._onViewToggle);
     }
 
     yield this.selectView(DEFAULT_DETAILS_SUBVIEW);
     yield this.setAvailableViews();
 
+    PerformanceController.on(EVENTS.RECORDING_STOPPED, 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)) {
       component.initialized && (yield component.view.destroy());
     }
 
+    PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
+    PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
     PerformanceController.off(EVENTS.PREF_CHANGED, this.setAvailableViews);
   }),
 
   /**
    * Sets the possible views based off of prefs 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.`.
    */
   setAvailableViews: Task.async(function* () {
     for (let [name, { view, pref }] of Iterator(this.components)) {
-      if (!pref) {
-        continue;
-      }
-      let value = PerformanceController.getPref(pref);
-      $(`toolbarbutton[data-view=${name}]`).hidden = !value;
+      let recording = PerformanceController.getCurrentRecording();
+
+      let isRecorded = recording && !recording.isRecording();
+      let isEnabled = !pref || PerformanceController.getPref(pref);
+      $(`toolbarbutton[data-view=${name}]`).hidden = !isRecorded || !isEnabled;
 
       // If the view is currently selected and not enabled, go back to the
       // default view.
-      if (!value && this.isViewSelected(view)) {
+      if (!isEnabled && this.isViewSelected(view)) {
         yield this.selectView(DEFAULT_DETAILS_SUBVIEW);
       }
     }
   }),
 
   /**
    * Select one of the DetailView's subviews to be rendered,
    * hiding the others.
@@ -155,16 +160,23 @@ let DetailsView = {
     // attempt to render if recording is in progress or does not exist.
     let recording = PerformanceController.getCurrentRecording();
     if (recording && !recording.isRecording()) {
       component.view.shouldUpdateWhenShown = true;
     }
   }),
 
   /**
+   * Called when recording stops or is selected.
+   */
+  _onRecordingStoppedOrSelected: function(_, recording) {
+    this.setAvailableViews();
+  },
+
+  /**
    * Called when a view button is clicked.
    */
   _onViewToggle: function (e) {
     this.selectView(e.target.getAttribute("data-view"));
   },
 
   toString: () => "[object DetailsView]"
 };