diff options
Diffstat (limited to 'devtools/client/performance/components/waterfall-tree.js')
-rw-r--r-- | devtools/client/performance/components/waterfall-tree.js | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/devtools/client/performance/components/waterfall-tree.js b/devtools/client/performance/components/waterfall-tree.js new file mode 100644 index 000000000..031c4facf --- /dev/null +++ b/devtools/client/performance/components/waterfall-tree.js @@ -0,0 +1,167 @@ +/* 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 { createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react"); +const Tree = createFactory(require("devtools/client/shared/components/tree")); +const WaterfallTreeRow = createFactory(require("./waterfall-tree-row")); + +// px - keep in sync with var(--waterfall-tree-row-height) in performance.css +const WATERFALL_TREE_ROW_HEIGHT = 15; + +/** + * Checks if a given marker is in the specified time range. + * + * @param object e + * The marker containing the { start, end } timestamps. + * @param number start + * The earliest allowed time. + * @param number end + * The latest allowed time. + * @return boolean + * True if the marker fits inside the specified time range. + */ +function isMarkerInRange(e, start, end) { + let mStart = e.start | 0; + let mEnd = e.end | 0; + + return ( + // bounds inside + (mStart >= start && mEnd <= end) || + // bounds outside + (mStart < start && mEnd > end) || + // overlap start + (mStart < start && mEnd >= start && mEnd <= end) || + // overlap end + (mEnd > end && mStart >= start && mStart <= end) + ); +} + +const WaterfallTree = createClass({ + displayName: "WaterfallTree", + + propTypes: { + marker: PropTypes.object.isRequired, + startTime: PropTypes.number.isRequired, + endTime: PropTypes.number.isRequired, + dataScale: PropTypes.number.isRequired, + sidebarWidth: PropTypes.number.isRequired, + waterfallWidth: PropTypes.number.isRequired, + onFocus: PropTypes.func, + }, + + getInitialState() { + return { + focused: null, + expanded: new Set() + }; + }, + + _getRoots(node) { + let roots = this.props.marker.submarkers || []; + return roots.filter(this._filter); + }, + + /** + * Find the parent node of 'node' with a depth-first search of the marker tree + */ + _getParent(node) { + function findParent(marker) { + if (marker.submarkers) { + for (let submarker of marker.submarkers) { + if (submarker === node) { + return marker; + } + + let parent = findParent(submarker); + if (parent) { + return parent; + } + } + } + + return null; + } + + let rootMarker = this.props.marker; + let parent = findParent(rootMarker); + + // We are interested only in parent markers that are rendered, + // which rootMarker is not. Return null if the parent is rootMarker. + return parent !== rootMarker ? parent : null; + }, + + _getChildren(node) { + let submarkers = node.submarkers || []; + return submarkers.filter(this._filter); + }, + + _getKey(node) { + return `marker-${node.index}`; + }, + + _isExpanded(node) { + return this.state.expanded.has(node); + }, + + _onExpand(node) { + this.setState(state => { + let expanded = new Set(state.expanded); + expanded.add(node); + return { expanded }; + }); + }, + + _onCollapse(node) { + this.setState(state => { + let expanded = new Set(state.expanded); + expanded.delete(node); + return { expanded }; + }); + }, + + _onFocus(node) { + this.setState({ focused: node }); + if (this.props.onFocus) { + this.props.onFocus(node); + } + }, + + _filter(node) { + let { startTime, endTime } = this.props; + return isMarkerInRange(node, startTime, endTime); + }, + + _renderItem(marker, level, focused, arrow, expanded) { + let { startTime, dataScale, sidebarWidth } = this.props; + return WaterfallTreeRow({ + marker, + level, + arrow, + expanded, + focused, + startTime, + dataScale, + sidebarWidth + }); + }, + + render() { + return Tree({ + getRoots: this._getRoots, + getParent: this._getParent, + getChildren: this._getChildren, + getKey: this._getKey, + isExpanded: this._isExpanded, + onExpand: this._onExpand, + onCollapse: this._onCollapse, + onFocus: this._onFocus, + renderItem: this._renderItem, + focused: this.state.focused, + itemHeight: WATERFALL_TREE_ROW_HEIGHT + }); + } +}); + +module.exports = WaterfallTree; |