Bug 1120699 - Implement panel state toggling in performance tool. r=vp

From f35d8e97b0b87d938c7bd37eb64ec5f5c32d8352 Mon Sep 17 00:00:00 2001
This commit is contained in:
Jordan Santell 2015-02-03 15:35:48 -08:00
parent 9163fb6e1e
commit a2f3e16216
7 changed files with 302 additions and 97 deletions

View File

@ -7,6 +7,25 @@
* Master view handler for the performance tool.
*/
let PerformanceView = {
_state: null,
// Mapping of state to selectors for different panes
// of the main profiler view. Used in `PerformanceView.setState()`
states: {
empty: [
{ deck: "#performance-view", pane: "#empty-notice" }
],
recording: [
{ deck: "#performance-view", pane: "#performance-view-content" },
{ deck: "#details-pane-container", pane: "#recording-notice" }
],
recorded: [
{ deck: "#performance-view", pane: "#performance-view-content" },
{ deck: "#details-pane-container", pane: "#details-pane" }
]
},
/**
* Sets up the view with event binding and main subviews.
*/
@ -18,13 +37,20 @@ let PerformanceView = {
this._onImportButtonClick = this._onImportButtonClick.bind(this);
this._lockRecordButton = this._lockRecordButton.bind(this);
this._unlockRecordButton = this._unlockRecordButton.bind(this);
this._onRecordingSelected = this._onRecordingSelected.bind(this);
this._onRecordingStopped = this._onRecordingStopped.bind(this);
this._recordButton.addEventListener("click", this._onRecordButtonClick);
for (let button of $$(".record-button")) {
button.addEventListener("click", this._onRecordButtonClick);
}
this._importButton.addEventListener("click", this._onImportButtonClick);
// Bind to controller events to unlock the record button
PerformanceController.on(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._unlockRecordButton);
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
this.setState("empty");
return promise.all([
RecordingsView.initialize(),
@ -38,11 +64,14 @@ let PerformanceView = {
* Unbinds events and destroys subviews.
*/
destroy: function () {
this._recordButton.removeEventListener("click", this._onRecordButtonClick);
for (let button of $$(".record-button")) {
button.removeEventListener("click", this._onRecordButtonClick);
}
this._importButton.removeEventListener("click", this._onImportButtonClick);
PerformanceController.off(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._unlockRecordButton);
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
return promise.all([
RecordingsView.destroy(),
@ -52,6 +81,29 @@ let PerformanceView = {
]);
},
/**
* Sets the state of the profiler view. Possible options are "empty",
* "recording", "recorded".
*/
setState: function (state) {
let viewConfig = this.states[state];
if (!viewConfig) {
throw new Error(`Invalid state for PerformanceView: ${state}`);
}
for (let { deck, pane } of viewConfig) {
$(deck).selectedPanel = $(pane);
}
this._state = state;
},
/**
* Returns the state of the PerformanceView.
*/
getState: function () {
return this._state;
},
/**
* Adds the `locked` attribute on the record button. This prevents it
* from being clicked while recording is started or stopped.
@ -67,6 +119,20 @@ let PerformanceView = {
this._recordButton.removeAttribute("locked");
},
/**
* When a recording is complete.
*/
_onRecordingStopped: function (_, recording) {
this._unlockRecordButton();
// If this recording stopped is the current recording, set the
// state to "recorded". A stopped recording doesn't necessarily
// have to be the current recording (console.profileEnd, for example)
if (recording === PerformanceController.getCurrentRecording()) {
this.setState("recorded");
}
},
/**
* Handler for clicking the record button.
*/
@ -94,6 +160,17 @@ let PerformanceView = {
if (fp.show() == Ci.nsIFilePicker.returnOK) {
this.emit(EVENTS.UI_IMPORT_RECORDING, fp.file);
}
},
/**
* Fired when a recording is selected. Used to toggle the profiler view state.
*/
_onRecordingSelected: function (_, recording) {
if (recording.isRecording()) {
this.setState("recording");
} else {
this.setState("recorded");
}
}
};

View File

@ -65,7 +65,7 @@
<hbox id="recordings-controls"
class="devtools-toolbarbutton-group">
<toolbarbutton id="record-button"
class="devtools-toolbarbutton"
class="devtools-toolbarbutton record-button"
tooltiptext="&profilerUI.recordButton.tooltip;"/>
<toolbarbutton id="import-button"
class="devtools-toolbarbutton"
@ -111,98 +111,122 @@
</hbox>
</toolbar>
<vbox id="overview-pane">
<hbox id="markers-overview"/>
<hbox id="memory-overview"/>
<hbox id="time-framerate"/>
</vbox>
<deck id="details-pane" flex="1">
<hbox id="waterfall-view" flex="1">
<vbox id="waterfall-breakdown" flex="1" />
<splitter class="devtools-side-splitter"/>
<vbox id="waterfall-details"
class="theme-sidebar"
width="150"
height="150"/>
<deck id="performance-view" flex="1">
<hbox id="empty-notice"
class="notice-container"
align="center"
pack="center"
flex="1">
<label value="&profilerUI.emptyNotice1;"/>
<button class="devtools-toolbarbutton record-button"
standalone="true" />
<label value="&profilerUI.emptyNotice2;"/>
</hbox>
<vbox id="performance-view-content" flex="1">
<vbox id="overview-pane">
<hbox id="markers-overview"/>
<hbox id="memory-overview"/>
<hbox id="time-framerate"/>
</vbox>
<deck id="details-pane-container" flex="1">
<hbox id="recording-notice"
class="notice-container"
align="center"
pack="center"
flex="1">
<label value="&profilerUI.stopNotice1;"/>
<button class="devtools-toolbarbutton record-button"
standalone="true"
checked="true" />
<label value="&profilerUI.stopNotice2;"/>
</hbox>
<deck id="details-pane" flex="1">
<hbox id="waterfall-view" flex="1">
<vbox id="waterfall-breakdown" flex="1" />
<splitter class="devtools-side-splitter"/>
<vbox id="waterfall-details"
class="theme-sidebar"
width="150"
height="150"/>
</hbox>
<vbox id="js-calltree-view" flex="1">
<hbox class="call-tree-headers-container">
<label class="plain call-tree-header"
type="duration"
crop="end"
value="&profilerUI.table.totalDuration2;"/>
<label class="plain call-tree-header"
type="percentage"
crop="end"
value="&profilerUI.table.totalPercentage;"/>
<label class="plain call-tree-header"
type="self-duration"
crop="end"
value="&profilerUI.table.selfDuration2;"/>
<label class="plain call-tree-header"
type="self-percentage"
crop="end"
value="&profilerUI.table.selfPercentage;"/>
<label class="plain call-tree-header"
type="samples"
crop="end"
value="&profilerUI.table.samples;"/>
<label class="plain call-tree-header"
type="function"
crop="end"
value="&profilerUI.table.function;"/>
</hbox>
<vbox class="call-tree-cells-container" flex="1"/>
<vbox id="js-calltree-view" flex="1">
<hbox class="call-tree-headers-container">
<label class="plain call-tree-header"
type="duration"
crop="end"
value="&profilerUI.table.totalDuration2;"/>
<label class="plain call-tree-header"
type="percentage"
crop="end"
value="&profilerUI.table.totalPercentage;"/>
<label class="plain call-tree-header"
type="self-duration"
crop="end"
value="&profilerUI.table.selfDuration2;"/>
<label class="plain call-tree-header"
type="self-percentage"
crop="end"
value="&profilerUI.table.selfPercentage;"/>
<label class="plain call-tree-header"
type="samples"
crop="end"
value="&profilerUI.table.samples;"/>
<label class="plain call-tree-header"
type="function"
crop="end"
value="&profilerUI.table.function;"/>
</hbox>
<vbox class="call-tree-cells-container" flex="1"/>
</vbox>
<hbox id="js-flamegraph-view" flex="1">
</hbox>
<vbox id="memory-calltree-view" flex="1">
<hbox class="call-tree-headers-container">
<label class="plain call-tree-header"
type="duration"
crop="end"
value="&profilerUI.table.totalDuration2;"/>
<label class="plain call-tree-header"
type="percentage"
crop="end"
value="&profilerUI.table.totalPercentage;"/>
<label class="plain call-tree-header"
type="allocations"
crop="end"
value="&profilerUI.table.totalAlloc;"/>
<label class="plain call-tree-header"
type="self-duration"
crop="end"
value="&profilerUI.table.selfDuration2;"/>
<label class="plain call-tree-header"
type="self-percentage"
crop="end"
value="&profilerUI.table.selfPercentage;"/>
<label class="plain call-tree-header"
type="self-allocations"
crop="end"
value="&profilerUI.table.selfAlloc;"/>
<label class="plain call-tree-header"
type="samples"
crop="end"
value="&profilerUI.table.samples;"/>
<label class="plain call-tree-header"
type="function"
crop="end"
value="&profilerUI.table.function;"/>
</hbox>
<vbox class="call-tree-cells-container" flex="1"/>
</vbox>
<hbox id="memory-flamegraph-view" flex="1">
</hbox>
</deck>
</deck>
</vbox>
<hbox id="js-flamegraph-view" flex="1">
</hbox>
<vbox id="memory-calltree-view" flex="1">
<hbox class="call-tree-headers-container">
<label class="plain call-tree-header"
type="duration"
crop="end"
value="&profilerUI.table.totalDuration2;"/>
<label class="plain call-tree-header"
type="percentage"
crop="end"
value="&profilerUI.table.totalPercentage;"/>
<label class="plain call-tree-header"
type="allocations"
crop="end"
value="&profilerUI.table.totalAlloc;"/>
<label class="plain call-tree-header"
type="self-duration"
crop="end"
value="&profilerUI.table.selfDuration2;"/>
<label class="plain call-tree-header"
type="self-percentage"
crop="end"
value="&profilerUI.table.selfPercentage;"/>
<label class="plain call-tree-header"
type="self-allocations"
crop="end"
value="&profilerUI.table.selfAlloc;"/>
<label class="plain call-tree-header"
type="samples"
crop="end"
value="&profilerUI.table.samples;"/>
<label class="plain call-tree-header"
type="function"
crop="end"
value="&profilerUI.table.function;"/>
</hbox>
<vbox class="call-tree-cells-container" flex="1"/>
</vbox>
<hbox id="memory-flamegraph-view" flex="1">
<!-- TODO: bug 1077461 -->
</hbox>
</deck>
</vbox>
</hbox>
</window>

View File

@ -52,6 +52,8 @@ support-files =
[browser_perf-shared-connection-02.js]
[browser_perf-shared-connection-03.js]
[browser_perf-ui-recording.js]
[browser_perf-recording-notices-01.js]
[browser_perf-recording-notices-02.js]
[browser_perf_recordings-io-01.js]
[browser_perf_recordings-io-02.js]
[browser_perf_recordings-io-03.js]

View File

@ -0,0 +1,36 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the recording notice panes are toggled in correct scenarios
* for initialization and a single recording.
*/
function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, $, PerformanceView, RecordingsView } = panel.panelWin;
let MAIN_CONTAINER = $("#performance-view");
let EMPTY = $("#empty-notice");
let CONTENT = $("#performance-view-content");
let CONTENT_CONTAINER = $("#details-pane-container");
let RECORDING = $("#recording-notice");
let DETAILS = $("#details-pane");
is(PerformanceView.getState(), "empty", "correct default state");
is(MAIN_CONTAINER.selectedPanel, EMPTY, "showing empty panel on load");
yield startRecording(panel);
is(PerformanceView.getState(), "recording", "correct state during recording");
is(MAIN_CONTAINER.selectedPanel, CONTENT, "showing main view with timeline");
is(CONTENT_CONTAINER.selectedPanel, RECORDING, "showing recording panel");
yield stopRecording(panel);
is(PerformanceView.getState(), "recorded", "correct state after recording");
is(MAIN_CONTAINER.selectedPanel, CONTENT, "showing main view with timeline");
is(CONTENT_CONTAINER.selectedPanel, DETAILS, "showing rendered graphs");
yield teardown(panel);
finish();
}

View File

@ -0,0 +1,47 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the recording notice panes are toggled when going between
* a completed recording and an in-progress recording.
*/
function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, $, PerformanceController, PerformanceView, RecordingsView } = panel.panelWin;
let MAIN_CONTAINER = $("#performance-view");
let CONTENT = $("#performance-view-content");
let CONTENT_CONTAINER = $("#details-pane-container");
let RECORDING = $("#recording-notice");
let DETAILS = $("#details-pane");
yield startRecording(panel);
yield stopRecording(panel);
yield startRecording(panel);
is(PerformanceView.getState(), "recording", "correct state during recording");
is(MAIN_CONTAINER.selectedPanel, CONTENT, "showing main view with timeline");
is(CONTENT_CONTAINER.selectedPanel, RECORDING, "showing recording panel");
let select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
RecordingsView.selectedIndex = 0;
yield select;
is(PerformanceView.getState(), "recorded", "correct state during recording but selecting a completed recording");
is(MAIN_CONTAINER.selectedPanel, CONTENT, "showing main view with timeline");
is(CONTENT_CONTAINER.selectedPanel, DETAILS, "showing recorded panel");
select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
RecordingsView.selectedIndex = 1;
yield select;
is(PerformanceView.getState(), "recording", "correct state when switching back to recording in progress");
is(MAIN_CONTAINER.selectedPanel, CONTENT, "showing main view with timeline");
is(CONTENT_CONTAINER.selectedPanel, RECORDING, "showing recording panel");
yield stopRecording(panel);
yield teardown(panel);
finish();
}

View File

@ -34,10 +34,29 @@
-moz-padding-end: 8px;
}
/* Recording Notice */
#performance-view .notice-container {
font-size: 120%;
background-color: var(--theme-toolbar-background);
color: var(--theme-body-color);
padding-bottom: 20vh;
}
#performance-view .notice-container button {
min-width: 30px;
min-height: 28px;
margin: 0;
}
/* Overview Panel */
.notice-container button,
#record-button {
list-style-image: url(profiler-stopwatch.svg);
}
.notice-container button[checked],
#record-button[checked] {
list-style-image: url(profiler-stopwatch-checked.svg);
}

View File

@ -891,8 +891,7 @@
.theme-light .scrollbutton-up > .toolbarbutton-icon,
.theme-light .scrollbutton-down > .toolbarbutton-icon,
.theme-light #black-boxed-message-button .button-icon,
.theme-light #profiling-notice-button .button-icon,
.theme-light #canvas-debugging-empty-notice-button .button-icon,
.theme-light .notice-container button .button-icon,
.theme-light #requests-menu-perf-notice-button .button-icon,
.theme-light #requests-menu-network-summary-button .button-icon,
.theme-light .event-tooltip-debugger-icon,
@ -906,7 +905,8 @@
.theme-light .devtools-tab[icon-invertable][selected] > image,
.theme-light .devtools-tab[icon-invertable][highlighted] > image,
.theme-light #record-snapshot[checked] > image,
.theme-light #profiler-start[checked] > image {
.theme-light #profiler-start[checked] > image,
.theme-light .notice-container button[checked] .button-icon {
filter: none !important;
}
@ -958,4 +958,4 @@
to {
transform: rotate(360deg);
}
}
}