Bug 1131577 - Waterfall view can scroll outside the visible bounds if the flamegraph view was initialized before a recording was started, r=jsantell
--- 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]"
};