mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-30 05:35:31 +00:00
Bug 1126882 - Detail views in the new performance tool should share a base class to avoid code duplication, r=jsantell
This commit is contained in:
parent
d4ea5b902a
commit
470f708065
@ -98,6 +98,7 @@ browser.jar:
|
||||
content/browser/devtools/performance/views/overview.js (performance/views/overview.js)
|
||||
content/browser/devtools/performance/views/toolbar.js (performance/views/toolbar.js)
|
||||
content/browser/devtools/performance/views/details.js (performance/views/details.js)
|
||||
content/browser/devtools/performance/views/details-subview.js (performance/views/details-abstract-subview.js)
|
||||
content/browser/devtools/performance/views/details-call-tree.js (performance/views/details-call-tree.js)
|
||||
content/browser/devtools/performance/views/details-waterfall.js (performance/views/details-waterfall.js)
|
||||
content/browser/devtools/performance/views/details-flamegraph.js (performance/views/details-flamegraph.js)
|
||||
|
@ -19,6 +19,7 @@
|
||||
<script type="application/javascript" src="performance/recording-model.js"/>
|
||||
<script type="application/javascript" src="performance/views/overview.js"/>
|
||||
<script type="application/javascript" src="performance/views/toolbar.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-subview.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-call-tree.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-waterfall.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-flamegraph.js"/>
|
||||
|
@ -0,0 +1,86 @@
|
||||
/* 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";
|
||||
|
||||
/**
|
||||
* A base class from which all detail views inherit.
|
||||
*/
|
||||
let 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);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onOverviewRangeChange);
|
||||
DetailsView.on(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
},
|
||||
|
||||
/**
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
clearNamedTimeout("range-change-debounce");
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onOverviewRangeChange);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onOverviewRangeChange);
|
||||
DetailsView.off(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
},
|
||||
|
||||
/**
|
||||
* Amount of time (in milliseconds) to wait until this view gets updated,
|
||||
* when the range is changed in the overview.
|
||||
*/
|
||||
rangeChangeDebounceTime: 0,
|
||||
|
||||
/**
|
||||
* Flag specifying if this view should be updated when selected. This will
|
||||
* be set to true, for example, when the range changes in the overview and
|
||||
* this view is not currently visible.
|
||||
*/
|
||||
shouldUpdateWhenShown: false,
|
||||
|
||||
/**
|
||||
* Called when recording stops or is selected.
|
||||
*/
|
||||
_onRecordingStoppedOrSelected: function(_, recording) {
|
||||
if (!recording.isRecording()) {
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a range is selected or cleared in the OverviewView.
|
||||
*/
|
||||
_onOverviewRangeChange: function (_, interval) {
|
||||
if (DetailsView.isViewSelected(this)) {
|
||||
let debounced = () => this.render(interval);
|
||||
setNamedTimeout("range-change-debounce", this.rangeChangeDebounceTime, debounced);
|
||||
} else {
|
||||
this.shouldUpdateWhenShown = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a view is selected in the DetailsView.
|
||||
*/
|
||||
_onDetailsViewSelected: function() {
|
||||
if (DetailsView.isViewSelected(this) && this.shouldUpdateWhenShown) {
|
||||
this.render(OverviewView.getTimeInterval());
|
||||
this.shouldUpdateWhenShown = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenient way of emitting events from the view.
|
||||
*/
|
||||
EventEmitter.decorate(DetailsSubview);
|
@ -3,42 +3,31 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const CALLTREE_UPDATE_DEBOUNCE = 50; // ms
|
||||
|
||||
/**
|
||||
* CallTree view containing profiler call tree, controlled by DetailsView.
|
||||
*/
|
||||
let CallTreeView = {
|
||||
let CallTreeView = Heritage.extend(DetailsSubview, {
|
||||
rangeChangeDebounceTime: 50, // ms
|
||||
|
||||
/**
|
||||
* Sets up the view with event binding.
|
||||
*/
|
||||
initialize: function () {
|
||||
this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
|
||||
this._onRangeChange = this._onRangeChange.bind(this);
|
||||
this._onDetailsViewSelected = this._onDetailsViewSelected.bind(this);
|
||||
DetailsSubview.initialize.call(this);
|
||||
|
||||
this._onPrefChanged = this._onPrefChanged.bind(this);
|
||||
this._onLink = this._onLink.bind(this);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
DetailsView.on(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
},
|
||||
|
||||
/**
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
clearNamedTimeout("calltree-update");
|
||||
DetailsSubview.destroy.call(this);
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
DetailsView.off(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -57,38 +46,6 @@ let CallTreeView = {
|
||||
this.emit(EVENTS.CALL_TREE_RENDERED);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when recording is stopped or has been selected.
|
||||
*/
|
||||
_onRecordingStoppedOrSelected: function (_, recording) {
|
||||
if (!recording.isRecording()) {
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a range is selected or cleared in the OverviewView.
|
||||
*/
|
||||
_onRangeChange: function (_, interval) {
|
||||
if (DetailsView.isViewSelected(this)) {
|
||||
let debounced = () => this.render(interval);
|
||||
setNamedTimeout("calltree-update", CALLTREE_UPDATE_DEBOUNCE, debounced);
|
||||
} else {
|
||||
this._dirty = true;
|
||||
this._interval = interval;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a view is selected in the DetailsView.
|
||||
*/
|
||||
_onDetailsViewSelected: function() {
|
||||
if (DetailsView.isViewSelected(this) && this._dirty) {
|
||||
this.render(this._interval);
|
||||
this._dirty = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired on the "link" event for the call tree in this container.
|
||||
*/
|
||||
@ -155,12 +112,7 @@ let CallTreeView = {
|
||||
this.render(OverviewView.getTimeInterval());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenient way of emitting events from the view.
|
||||
*/
|
||||
EventEmitter.decorate(CallTreeView);
|
||||
});
|
||||
|
||||
/**
|
||||
* Opens/selects the debugger in this toolbox and jumps to the specified
|
||||
|
@ -7,40 +7,29 @@
|
||||
* FlameGraph view containing a pyramid-like visualization of a profile,
|
||||
* controlled by DetailsView.
|
||||
*/
|
||||
let FlameGraphView = {
|
||||
let FlameGraphView = Heritage.extend(DetailsSubview, {
|
||||
/**
|
||||
* Sets up the view with event binding.
|
||||
*/
|
||||
initialize: Task.async(function* () {
|
||||
this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
|
||||
this._onRangeChange = this._onRangeChange.bind(this);
|
||||
this._onRangeChangeInGraph = this._onRangeChangeInGraph.bind(this);
|
||||
this._onDetailsViewSelected = this._onDetailsViewSelected.bind(this);
|
||||
DetailsSubview.initialize.call(this);
|
||||
|
||||
this.graph = new FlameGraph($("#flamegraph-view"));
|
||||
this.graph.timelineTickUnits = L10N.getStr("graphs.ms");
|
||||
yield this.graph.ready();
|
||||
|
||||
this.graph.on("selecting", this._onRangeChangeInGraph);
|
||||
this._onRangeChangeInGraph = this._onRangeChangeInGraph.bind(this);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
DetailsView.on(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
this.graph.on("selecting", this._onRangeChangeInGraph);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
this.graph.off("selecting", this._onRangeChangeInGraph);
|
||||
DetailsSubview.destroy.call(this);
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
DetailsView.off(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
this.graph.off("selecting", this._onRangeChangeInGraph);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -51,42 +40,28 @@ let FlameGraphView = {
|
||||
*/
|
||||
render: function (interval={}) {
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
let startTime = interval.startTime || 0;
|
||||
let endTime = interval.endTime || recording.getDuration();
|
||||
this.graph.setViewRange({ startTime, endTime });
|
||||
this.emit(EVENTS.FLAMEGRAPH_RENDERED);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when recording is stopped or selected.
|
||||
*/
|
||||
_onRecordingStoppedOrSelected: function (_, recording) {
|
||||
if (recording.isRecording()) {
|
||||
return;
|
||||
}
|
||||
let duration = recording.getDuration();
|
||||
let profile = recording.getProfile();
|
||||
let samples = profile.threads[0].samples;
|
||||
|
||||
let data = FlameGraphUtils.createFlameGraphDataFromSamples(samples, {
|
||||
flattenRecursion: Prefs.flattenTreeRecursion,
|
||||
filterFrames: !Prefs.showPlatformData && FrameNode.isContent,
|
||||
showIdleBlocks: Prefs.showIdleBlocks && L10N.getStr("table.idle")
|
||||
});
|
||||
let startTime = 0;
|
||||
let endTime = recording.getDuration();
|
||||
this.graph.setData({ data, bounds: { startTime, endTime } });
|
||||
this.render();
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a range is selected or cleared in the OverviewView.
|
||||
*/
|
||||
_onRangeChange: function (_, interval) {
|
||||
if (DetailsView.isViewSelected(this)) {
|
||||
this.render(interval);
|
||||
} else {
|
||||
this._dirty = true;
|
||||
this._interval = interval;
|
||||
}
|
||||
this.graph.setData({ data,
|
||||
bounds: {
|
||||
startTime: 0,
|
||||
endTime: duration
|
||||
},
|
||||
visible: {
|
||||
startTime: interval.startTime || 0,
|
||||
endTime: interval.endTime || duration
|
||||
}
|
||||
});
|
||||
|
||||
this.emit(EVENTS.FLAMEGRAPH_RENDERED);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -95,20 +70,5 @@ let FlameGraphView = {
|
||||
_onRangeChangeInGraph: function () {
|
||||
let interval = this.graph.getViewRange();
|
||||
OverviewView.setTimeInterval(interval, { stopPropagation: true });
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a view is selected in the DetailsView.
|
||||
*/
|
||||
_onDetailsViewSelected: function() {
|
||||
if (DetailsView.isViewSelected(this) && this._dirty) {
|
||||
this.render(this._interval);
|
||||
this._dirty = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenient way of emitting events from the view.
|
||||
*/
|
||||
EventEmitter.decorate(FlameGraphView);
|
||||
});
|
||||
|
@ -3,56 +3,45 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const WATERFALL_UPDATE_DEBOUNCE = 10; // ms
|
||||
|
||||
/**
|
||||
* Waterfall view containing the timeline markers, controlled by DetailsView.
|
||||
*/
|
||||
let WaterfallView = {
|
||||
let WaterfallView = Heritage.extend(DetailsSubview, {
|
||||
rangeChangeDebounceTime: 10, // ms
|
||||
|
||||
/**
|
||||
* Sets up the view with event binding.
|
||||
*/
|
||||
initialize: Task.async(function *() {
|
||||
this._onRecordingStarted = this._onRecordingStarted.bind(this);
|
||||
this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
|
||||
this._onRangeChange = this._onRangeChange.bind(this);
|
||||
this._onDetailsViewSelected = this._onDetailsViewSelected.bind(this);
|
||||
this._onMarkerSelected = this._onMarkerSelected.bind(this);
|
||||
this._onResize = this._onResize.bind(this);
|
||||
initialize: function () {
|
||||
DetailsSubview.initialize.call(this);
|
||||
|
||||
this.waterfall = new Waterfall($("#waterfall-breakdown"), $("#details-pane"), TIMELINE_BLUEPRINT);
|
||||
this.details = new MarkerDetails($("#waterfall-details"), $("#waterfall-view > splitter"));
|
||||
|
||||
this._onRecordingStarted = this._onRecordingStarted.bind(this);
|
||||
this._onMarkerSelected = this._onMarkerSelected.bind(this);
|
||||
this._onResize = this._onResize.bind(this);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
|
||||
this.waterfall.on("selected", this._onMarkerSelected);
|
||||
this.waterfall.on("unselected", this._onMarkerSelected);
|
||||
this.details.on("resize", this._onResize);
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
DetailsView.on(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
|
||||
this.waterfall.recalculateBounds();
|
||||
}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
clearNamedTimeout("waterfall-update");
|
||||
DetailsSubview.destroy.call(this);
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
|
||||
this.waterfall.off("selected", this._onMarkerSelected);
|
||||
this.waterfall.off("unselected", this._onMarkerSelected);
|
||||
this.details.off("resize", this._onResize);
|
||||
|
||||
PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
|
||||
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
|
||||
OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
|
||||
DetailsView.off(EVENTS.DETAILS_VIEW_SELECTED, this._onDetailsViewSelected);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -77,38 +66,6 @@ let WaterfallView = {
|
||||
this.waterfall.clearView();
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when recording stops or is selected.
|
||||
*/
|
||||
_onRecordingStoppedOrSelected: function (_, recording) {
|
||||
if (!recording.isRecording()) {
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a range is selected or cleared in the OverviewView.
|
||||
*/
|
||||
_onRangeChange: function (_, interval) {
|
||||
if (DetailsView.isViewSelected(this)) {
|
||||
let debounced = () => this.render(interval);
|
||||
setNamedTimeout("waterfall-update", WATERFALL_UPDATE_DEBOUNCE, debounced);
|
||||
} else {
|
||||
this._dirty = true;
|
||||
this._interval = interval;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired when a view is selected in the DetailsView.
|
||||
*/
|
||||
_onDetailsViewSelected: function() {
|
||||
if (DetailsView.isViewSelected(this) && this._dirty) {
|
||||
this.render(this._interval);
|
||||
this._dirty = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a marker is selected in the waterfall view,
|
||||
* updating the markers detail view.
|
||||
@ -132,9 +89,4 @@ let WaterfallView = {
|
||||
this.waterfall.recalculateBounds();
|
||||
this.render();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenient way of emitting events from the view.
|
||||
*/
|
||||
EventEmitter.decorate(WaterfallView);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user