@@ -10,6 +10,7 @@ import * as Platform from '../../core/platform/platform.js';
1010import * as SDK from '../../core/sdk/sdk.js' ;
1111import * as Trace from '../../models/trace/trace.js' ;
1212import * as ThirdPartyWeb from '../../third_party/third-party-web/third-party-web.js' ;
13+ import * as Buttons from '../../ui/components/buttons/buttons.js' ;
1314import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js' ;
1415import * as Components from '../../ui/legacy/components/utils/utils.js' ;
1516import * as UI from '../../ui/legacy/legacy.js' ;
@@ -19,7 +20,7 @@ import {ActiveFilters} from './ActiveFilters.js';
1920import * as Extensions from './extensions/extensions.js' ;
2021import { Tracker } from './FreshRecording.js' ;
2122import { targetForEvent } from './TargetForEvent.js' ;
22- import type { ThirdPartyTreeViewWidget } from './ThirdPartyTreeView.js' ;
23+ import * as ThirdPartyTreeView from './ThirdPartyTreeView.js' ;
2324import { TimelineRegExp } from './TimelineFilters.js' ;
2425import { rangeForSelection , type TimelineSelection } from './TimelineSelection.js' ;
2526import { TimelineUIUtils } from './TimelineUIUtils.js' ;
@@ -144,6 +145,14 @@ const UIStrings = {
144145 * @description Text for Match whole word button
145146 */
146147 matchWholeWord : 'Match whole word' ,
148+ /**
149+ * @description Text for bottom up tree button
150+ */
151+ bottomUp : 'Bottom-up' ,
152+ /**
153+ * @description Text referring to view bottom up tree
154+ */
155+ viewBottomUp : 'View Bottom-up' ,
147156 /**
148157 * @description Text referring to a 1st party entity
149158 */
@@ -654,11 +663,13 @@ export namespace TimelineTreeView {
654663 export const enum Events {
655664 TREE_ROW_HOVERED = 'TreeRowHovered' ,
656665 THIRD_PARTY_ROW_HOVERED = 'ThirdPartyRowHovered' ,
666+ BOTTOM_UP_BUTTON_CLICKED = 'BottomUpButtonClicked' ,
657667 }
658668
659669 export interface EventTypes {
660670 [ Events . TREE_ROW_HOVERED ] : Trace . Extras . TraceTree . Node | null ;
661671 [ Events . THIRD_PARTY_ROW_HOVERED ] : Trace . Types . Events . Event [ ] | null ;
672+ [ Events . BOTTOM_UP_BUTTON_CLICKED ] : Trace . Extras . TraceTree . Node | null ;
662673 }
663674}
664675
@@ -708,8 +719,8 @@ export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<Gri
708719 }
709720
710721 // Include badges with the name, if relevant.
711- if ( columnId === 'site' && ( this . treeView as ThirdPartyTreeViewWidget ) ) {
712- const thirdPartyTree = ( this . treeView as ThirdPartyTreeViewWidget ) ;
722+ if ( columnId === 'site' && this . treeView instanceof ThirdPartyTreeView . ThirdPartyTreeViewWidget ) {
723+ const thirdPartyTree = this . treeView ;
713724 let badgeText = '' ;
714725
715726 if ( thirdPartyTree . nodeIsFirstParty ( this . profileNode ) ) {
@@ -753,7 +764,8 @@ export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<Gri
753764 let maxTime : number | undefined ;
754765 let event : Trace . Types . Events . Event | null ;
755766 let isSize = false ;
756- const thirdPartyView = this . treeView as ThirdPartyTreeViewWidget ;
767+ let showBottomUpButton = false ;
768+ const thirdPartyView = this . treeView as ThirdPartyTreeView . ThirdPartyTreeViewWidget ;
757769 switch ( columnId ) {
758770 case 'start-time' : {
759771 event = this . profileNode . event ;
@@ -769,6 +781,7 @@ export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<Gri
769781 value = this . profileNode . selfTime ;
770782 maxTime = this . maxSelfTime ;
771783 showPercents = true ;
784+ showBottomUpButton = thirdPartyView instanceof ThirdPartyTreeView . ThirdPartyTreeViewWidget ;
772785 break ;
773786 case 'total' :
774787 value = this . profileNode . totalTime ;
@@ -804,8 +817,38 @@ export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<Gri
804817 cell . createChild ( 'div' , 'background-bar-container' ) . createChild ( 'div' , 'background-bar' ) . style . width =
805818 ( value * 100 / maxTime ) . toFixed ( 1 ) + '%' ;
806819 }
820+ // Generate button on hover for 3P self time cell.
821+ if ( showBottomUpButton ) {
822+ this . generateBottomUpButton ( cell ) ;
823+ }
807824 return cell ;
808825 }
826+
827+ // Generates bottom up tree hover button and appends it to the provided cell element.
828+ private generateBottomUpButton ( cell : HTMLElement ) : void {
829+ const buttonContainer = document . createElement ( 'div' ) ;
830+ buttonContainer . className = 'button-container' ;
831+ cell . classList . add ( 'hover-bottom-up-button' ) ;
832+
833+ const button = new Buttons . Button . Button ( ) ;
834+ button . data = {
835+ variant : Buttons . Button . Variant . ICON ,
836+ iconName : 'account-tree' ,
837+ size : Buttons . Button . Size . SMALL ,
838+ toggledIconName : i18nString ( UIStrings . bottomUp ) ,
839+ } ;
840+ UI . ARIAUtils . setLabel ( button , i18nString ( UIStrings . viewBottomUp ) ) ;
841+ button . addEventListener ( 'click' , ( ) => this . #bottomUpButtonClicked( ) ) ;
842+ buttonContainer . appendChild ( button ) ;
843+ UI . Tooltip . Tooltip . install ( button , i18nString ( UIStrings . bottomUp ) ) ;
844+
845+ // Append the button to the last column
846+ cell . appendChild ( buttonContainer ) ;
847+ }
848+
849+ #bottomUpButtonClicked( ) : void {
850+ this . treeView . dispatchEventToListeners ( TimelineTreeView . Events . BOTTOM_UP_BUTTON_CLICKED , this . profileNode ) ;
851+ }
809852}
810853
811854export class TreeGridNode extends GridNode {
@@ -852,7 +895,7 @@ export class AggregatedTimelineTreeView extends TimelineTreeView {
852895 this . stackView . addEventListener ( TimelineStackView . Events . SELECTION_CHANGED , this . onStackViewSelectionChanged , this ) ;
853896 }
854897
855- setGroupBySettingForTests ( groupBy : AggregatedTimelineTreeView . GroupBy ) : void {
898+ setGroupBySetting ( groupBy : AggregatedTimelineTreeView . GroupBy ) : void {
856899 this . groupBySetting . set ( groupBy ) ;
857900 }
858901
0 commit comments