44// @flow
55
66import * as React from 'react' ;
7+ import memoize from 'memoize-immutable' ;
78
89import explicitConnect from 'firefox-profiler/utils/connect' ;
910import { FlameGraph } from 'firefox-profiler/components/flame-graph/FlameGraph' ;
11+ import { TreeView } from 'firefox-profiler/components/shared/TreeView' ;
12+ import { CallTreeEmptyReasons } from './CallTreeEmptyReasons' ;
13+ import {
14+ treeColumnsForTracingMs ,
15+ treeColumnsForSamples ,
16+ treeColumnsForBytes ,
17+ } from './columns' ;
1018
1119import {
1220 getCategories ,
@@ -16,16 +24,22 @@ import {
1624 getProfileInterval ,
1725 getInnerWindowIDToPageMap ,
1826 getProfileUsesMultipleStackTypes ,
27+ getCurrentTableViewOptions ,
28+ getPreviewSelectionIsBeingModified ,
1929} from 'firefox-profiler/selectors/profile' ;
2030import { selectedThreadSelectors } from 'firefox-profiler/selectors/per-thread' ;
2131import {
2232 getSelectedThreadsKey ,
2333 getInvertCallstack ,
34+ getSearchStringsAsRegExp ,
35+ getSelfWingView ,
2436} from 'firefox-profiler/selectors/url-state' ;
2537import {
2638 updateBottomBoxContentsAndMaybeOpen ,
2739 changeRightClickedFunctionIndex ,
40+ changeTableViewOptions ,
2841} from 'firefox-profiler/actions/profile-view' ;
42+ import { assertExhaustiveCheck } from 'firefox-profiler/utils/types' ;
2943
3044import type {
3145 Thread ,
@@ -41,12 +55,19 @@ import type {
4155 InnerWindowID ,
4256 Page ,
4357 SampleCategoriesAndSubcategories ,
58+ CallNodeDisplayData ,
59+ TableViewOptions ,
60+ WingViewType ,
4461} from 'firefox-profiler/types' ;
4562
4663import type { FlameGraphTiming } from 'firefox-profiler/profile-logic/flame-graph' ;
4764import type { CallNodeInfo } from 'firefox-profiler/profile-logic/call-node-info' ;
4865import type { CallTree } from 'firefox-profiler/profile-logic/call-tree' ;
4966
67+ import type {
68+ Column ,
69+ MaybeResizableColumn ,
70+ } from 'firefox-profiler/components/shared/TreeView' ;
5071import type { ConnectedProps } from 'firefox-profiler/utils/connect' ;
5172
5273type StateProps = {
@@ -68,26 +89,62 @@ type StateProps = {
6889 readonly ctssSamples : SamplesLikeTable ;
6990 readonly ctssSampleCategoriesAndSubcategories : SampleCategoriesAndSubcategories ;
7091 readonly displayStackType : boolean ;
92+ readonly searchStringsRegExp : RegExp | null ;
93+ readonly disableOverscan : boolean ;
94+ readonly tableViewOptions : TableViewOptions ;
95+ readonly view : WingViewType ;
7196} ;
7297
7398type DispatchProps = {
7499 readonly updateBottomBoxContentsAndMaybeOpen : typeof updateBottomBoxContentsAndMaybeOpen ;
75100 readonly changeRightClickedFunctionIndex : typeof changeRightClickedFunctionIndex ;
101+ readonly onTableViewOptionsChange : ( options : TableViewOptions ) => any ;
76102} ;
77103
78104type Props = ConnectedProps < { } , StateProps , DispatchProps > ;
79105
80106type LocalState = {
81107 selectedCallNodeIndex : IndexIntoCallNodeTable | null ;
82108 rightClickedCallNodeIndex : IndexIntoCallNodeTable | null ;
109+ expandedCallNodeIndexes : Array < IndexIntoCallNodeTable | null > ;
83110} ;
84111
85112class SelfWingImpl extends React . PureComponent < Props , LocalState > {
86113 override state : LocalState = {
87114 selectedCallNodeIndex : null ,
88115 rightClickedCallNodeIndex : null ,
116+ expandedCallNodeIndexes : [ ] ,
89117 } ;
90118
119+ _mainColumn : Column < CallNodeDisplayData > = {
120+ propName : 'name' ,
121+ titleL10nId : '' ,
122+ } ;
123+ _appendageColumn : Column < CallNodeDisplayData > = {
124+ propName : 'lib' ,
125+ titleL10nId : '' ,
126+ } ;
127+ _treeView : TreeView < CallNodeDisplayData > | null = null ;
128+ _takeTreeViewRef = ( treeView : TreeView < CallNodeDisplayData > | null ) => {
129+ this . _treeView = treeView ;
130+ } ;
131+
132+ _weightTypeToColumns = memoize (
133+ ( weightType : WeightType ) : MaybeResizableColumn < CallNodeDisplayData > [ ] => {
134+ switch ( weightType ) {
135+ case 'tracing-ms' :
136+ return treeColumnsForTracingMs ;
137+ case 'samples' :
138+ return treeColumnsForSamples ;
139+ case 'bytes' :
140+ return treeColumnsForBytes ;
141+ default :
142+ throw assertExhaustiveCheck ( weightType , 'Unhandled WeightType.' ) ;
143+ }
144+ } ,
145+ { cache : new Map ( ) }
146+ ) ;
147+
91148 override componentDidUpdate ( prevProps : Props , _prevState : LocalState ) {
92149 // Reset local selection when the call node info changes (e.g. different
93150 // function selected) since old call node indices are no longer valid.
@@ -98,6 +155,7 @@ class SelfWingImpl extends React.PureComponent<Props, LocalState> {
98155 this . setState ( {
99156 selectedCallNodeIndex : null ,
100157 rightClickedCallNodeIndex : null ,
158+ expandedCallNodeIndexes : [ ] ,
101159 } ) ;
102160 }
103161 }
@@ -119,6 +177,30 @@ class SelfWingImpl extends React.PureComponent<Props, LocalState> {
119177 changeRightClickedFunctionIndex ( threadsKey , funcIndex ) ;
120178 } ;
121179
180+ _onTreeViewSelectionChange = (
181+ callNodeIndex : IndexIntoCallNodeTable ,
182+ context : { source : 'keyboard' | 'pointer' }
183+ ) => {
184+ this . setState ( { selectedCallNodeIndex : callNodeIndex } , ( ) => {
185+ // Selection in this wing is local state, so the Redux-driven
186+ // scrollToSelectionGeneration mechanism used by the other wings does not
187+ // apply. Scroll directly when keyboard navigation moves the selection.
188+ if ( context . source === 'keyboard' && this . _treeView ) {
189+ this . _treeView . scrollSelectionIntoView ( ) ;
190+ }
191+ } ) ;
192+ } ;
193+
194+ _onTreeViewRightClickSelection = ( callNodeIndex : IndexIntoCallNodeTable ) => {
195+ this . _onRightClickedCallNodeChange ( callNodeIndex ) ;
196+ } ;
197+
198+ _onTreeViewExpandedNodesChange = (
199+ expandedCallNodeIndexes : Array < IndexIntoCallNodeTable | null >
200+ ) => {
201+ this . setState ( { expandedCallNodeIndexes } ) ;
202+ } ;
203+
122204 _onCallNodeEnterOrDoubleClick = (
123205 callNodeIndex : IndexIntoCallNodeTable | null
124206 ) => {
@@ -137,7 +219,7 @@ class SelfWingImpl extends React.PureComponent<Props, LocalState> {
137219 _nodeIndex : IndexIntoCallNodeTable
138220 ) => { } ;
139221
140- override render ( ) {
222+ _renderFlameGraph ( ) {
141223 const {
142224 thread,
143225 threadsKey,
@@ -194,6 +276,58 @@ class SelfWingImpl extends React.PureComponent<Props, LocalState> {
194276 />
195277 ) ;
196278 }
279+
280+ _renderCallTree ( ) {
281+ const {
282+ callTree,
283+ searchStringsRegExp,
284+ disableOverscan,
285+ maxStackDepthPlusOne,
286+ weightType,
287+ tableViewOptions,
288+ onTableViewOptionsChange,
289+ } = this . props ;
290+ const {
291+ selectedCallNodeIndex,
292+ rightClickedCallNodeIndex,
293+ expandedCallNodeIndexes,
294+ } = this . state ;
295+ if ( callTree . getRoots ( ) . length === 0 ) {
296+ return < CallTreeEmptyReasons /> ;
297+ }
298+ return (
299+ < TreeView
300+ tree = { callTree }
301+ fixedColumns = { this . _weightTypeToColumns ( weightType ) }
302+ mainColumn = { this . _mainColumn }
303+ appendageColumn = { this . _appendageColumn }
304+ onSelectionChange = { this . _onTreeViewSelectionChange }
305+ onRightClickSelection = { this . _onTreeViewRightClickSelection }
306+ onExpandedNodesChange = { this . _onTreeViewExpandedNodesChange }
307+ ref = { this . _takeTreeViewRef }
308+ selectedNodeId = { selectedCallNodeIndex }
309+ rightClickedNodeId = { rightClickedCallNodeIndex }
310+ expandedNodeIds = { expandedCallNodeIndexes }
311+ highlightRegExp = { searchStringsRegExp }
312+ disableOverscan = { disableOverscan }
313+ contextMenuId = "FunctionListContextMenu"
314+ maxNodeDepth = { maxStackDepthPlusOne }
315+ rowHeight = { 16 }
316+ indentWidth = { 10 }
317+ onEnterKey = { this . _onCallNodeEnterOrDoubleClick }
318+ onDoubleClick = { this . _onCallNodeEnterOrDoubleClick }
319+ viewOptions = { tableViewOptions }
320+ onViewOptionsChange = { onTableViewOptionsChange }
321+ />
322+ ) ;
323+ }
324+
325+ override render ( ) {
326+ if ( this . props . view === 'call-tree' ) {
327+ return this . _renderCallTree ( ) ;
328+ }
329+ return this . _renderFlameGraph ( ) ;
330+ }
197331}
198332
199333export const SelfWing = explicitConnect < { } , StateProps , DispatchProps > ( {
@@ -222,10 +356,16 @@ export const SelfWing = explicitConnect<{}, StateProps, DispatchProps>({
222356 state
223357 ) ,
224358 displayStackType : getProfileUsesMultipleStackTypes ( state ) ,
359+ searchStringsRegExp : getSearchStringsAsRegExp ( state ) ,
360+ disableOverscan : getPreviewSelectionIsBeingModified ( state ) ,
361+ tableViewOptions : getCurrentTableViewOptions ( state ) ,
362+ view : getSelfWingView ( state ) ,
225363 } ) ,
226364 mapDispatchToProps : {
227365 updateBottomBoxContentsAndMaybeOpen,
228366 changeRightClickedFunctionIndex,
367+ onTableViewOptionsChange : ( options : TableViewOptions ) =>
368+ changeTableViewOptions ( 'calltree' , options ) ,
229369 } ,
230370 component : SelfWingImpl ,
231371} ) ;
0 commit comments