Skip to content

Commit 27573a0

Browse files
Adriana IxbaDevtools-frontend LUCI CQ
authored andcommitted
[RPP] Create Bottom-up tree button on 3P table hover
Bottom up tree button on 3P table clicks and directs to a filtered bottom up tree. The tree is "GroupBy" Third Party and selects and expands the related 3P https://screencast.googleplex.com/cast/NTk4MTk5MzA5ODU0MzEwNHwxNjFjY2UxZC0wNw Bug:None Change-Id: I3b905f6e72749ebd7ab0e3eb401c4b0879392863 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6171828 Auto-Submit: Adriana Ixba <[email protected]> Reviewed-by: Jack Franklin <[email protected]> Commit-Queue: Jack Franklin <[email protected]>
1 parent 62dc149 commit 27573a0

File tree

8 files changed

+109
-15
lines changed

8 files changed

+109
-15
lines changed

config/gni/devtools_grd_files.gni

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ grd_files_release_sources = [
2020
"front_end/Images/accelerometer-left.png",
2121
"front_end/Images/accelerometer-right.png",
2222
"front_end/Images/accelerometer-top.png",
23+
"front_end/Images/account-tree.svg",
2324
"front_end/Images/align-content-center.svg",
2425
"front_end/Images/align-content-end.svg",
2526
"front_end/Images/align-content-space-around.svg",

config/gni/devtools_image_files.gni

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ devtools_svg_sources = [
2626
"3d-rotate.svg",
2727
"accelerometer-back.svg",
2828
"accelerometer-front.svg",
29+
"account-tree.svg",
2930
"align-content-center.svg",
3031
"align-content-end.svg",
3132
"align-content-space-around.svg",
Lines changed: 1 addition & 0 deletions
Loading

front_end/panels/timeline/TimelineDetailsView.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ export class TimelineDetailsView extends
132132
this.dispatchEventToListeners(TimelineTreeView.Events.THIRD_PARTY_ROW_HOVERED, node.data);
133133
});
134134

135+
this.#thirdPartyTree.addEventListener(
136+
TimelineTreeView.Events.BOTTOM_UP_BUTTON_CLICKED, node => this.#bottomUpClicked(node));
137+
135138
this.#networkRequestDetails =
136139
new TimelineComponents.NetworkRequestDetails.NetworkRequestDetails(this.detailsLinkifier);
137140

@@ -144,6 +147,34 @@ export class TimelineDetailsView extends
144147
this.lazySelectorStatsView = null;
145148
}
146149

150+
#bottomUpClicked(event: Common.EventTarget.EventTargetEvent<Trace.Extras.TraceTree.Node|null>): void {
151+
// Select bottom up tree.
152+
this.tabbedPane.selectTab(Tab.BottomUp, true, true);
153+
if (!(this.tabbedPane.visibleView instanceof BottomUpTimelineTreeView)) {
154+
return;
155+
}
156+
const bottomUp = this.tabbedPane.visibleView;
157+
const thirdPartyNodeSelected = event.data;
158+
if (!thirdPartyNodeSelected) {
159+
return;
160+
}
161+
// Group by 3P.
162+
bottomUp.setGroupBySetting(BottomUpTimelineTreeView.GroupBy.ThirdParties);
163+
bottomUp.refreshTree();
164+
165+
// Look for the matching node in the bottom up tree using selected node event data.
166+
const treeNode = bottomUp.eventToTreeNode.get(thirdPartyNodeSelected.event);
167+
if (!treeNode) {
168+
return;
169+
}
170+
bottomUp.selectProfileNode(treeNode, true);
171+
// Reveal/expand the bottom up tree grid node.
172+
const gridNode = bottomUp.dataGridNodeForTreeNode(treeNode);
173+
if (gridNode) {
174+
gridNode.expand();
175+
}
176+
}
177+
147178
#createContentWidget(): UI.Widget.VBox {
148179
const defaultDetailsContentWidget = new UI.Widget.VBox();
149180
defaultDetailsContentWidget.element.classList.add('timeline-details-view-body');

front_end/panels/timeline/TimelineFlameChartView.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,7 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
11291129
this.#selectedGroupName = null;
11301130
Common.EventTarget.removeEventListeners(this.eventListeners);
11311131
this.#selectedEvents = null;
1132+
this.#entityMapper = new Utils.EntityMapper.EntityMapper(this.#parsedTrace);
11321133
// order is important: |reset| needs to be called after the trace
11331134
// model has been set in the data providers.
11341135
this.mainDataProvider.setModel(this.#parsedTrace);
@@ -1138,9 +1139,7 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
11381139
this.updateSearchResults(false, false);
11391140
this.refreshMainFlameChart();
11401141
this.#updateFlameCharts();
1141-
this.#entityMapper = new Utils.EntityMapper.EntityMapper(this.#parsedTrace);
11421142
this.setMarkers(this.#parsedTrace);
1143-
this.#entityMapper = new Utils.EntityMapper.EntityMapper(this.#parsedTrace);
11441143
}
11451144

11461145
setInsights(

front_end/panels/timeline/TimelineTreeView.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ describeWithEnvironment('TimelineTreeView', function() {
154154
const startTime = Trace.Helpers.Timing.microToMilli(parsedTrace.Meta.traceBounds.min);
155155
const endTime = Trace.Helpers.Timing.microToMilli(parsedTrace.Meta.traceBounds.max);
156156
callTreeView.setRange(startTime, endTime);
157-
callTreeView.setGroupBySettingForTests(Timeline.TimelineTreeView.AggregatedTimelineTreeView.GroupBy.Category);
157+
callTreeView.setGroupBySetting(Timeline.TimelineTreeView.AggregatedTimelineTreeView.GroupBy.Category);
158158
callTreeView.setModelWithEvents(consoleTimings, parsedTrace);
159159
const tree = callTreeView.buildTree();
160160
const treeEntries = tree.children().entries();
@@ -176,7 +176,7 @@ describeWithEnvironment('TimelineTreeView', function() {
176176
const startTime = Trace.Helpers.Timing.microToMilli(parsedTrace.Meta.traceBounds.min);
177177
const endTime = Trace.Helpers.Timing.microToMilli(parsedTrace.Meta.traceBounds.max);
178178
callTreeView.setRange(startTime, endTime);
179-
callTreeView.setGroupBySettingForTests(Timeline.TimelineTreeView.AggregatedTimelineTreeView.GroupBy.Category);
179+
callTreeView.setGroupBySetting(Timeline.TimelineTreeView.AggregatedTimelineTreeView.GroupBy.Category);
180180
callTreeView.setModelWithEvents(consoleTimings, parsedTrace);
181181
const tree = callTreeView.buildTree();
182182
const treeEntries = tree.children().entries();
@@ -200,7 +200,7 @@ describeWithEnvironment('TimelineTreeView', function() {
200200
const endTime = Trace.Helpers.Timing.microToMilli(parsedTrace.Meta.traceBounds.max);
201201

202202
callTreeView.setRange(startTime, endTime);
203-
callTreeView.setGroupBySettingForTests(Timeline.TimelineTreeView.AggregatedTimelineTreeView.GroupBy.Domain);
203+
callTreeView.setGroupBySetting(Timeline.TimelineTreeView.AggregatedTimelineTreeView.GroupBy.Domain);
204204
callTreeView.setModelWithEvents(parsedTrace.Renderer.allTraceEntries, parsedTrace);
205205

206206
const tree = callTreeView.buildTree();
@@ -225,7 +225,7 @@ describeWithEnvironment('TimelineTreeView', function() {
225225
const endTime = Trace.Helpers.Timing.microToMilli(parsedTrace.Meta.traceBounds.max);
226226

227227
callTreeView.setRange(startTime, endTime);
228-
callTreeView.setGroupBySettingForTests(Timeline.TimelineTreeView.AggregatedTimelineTreeView.GroupBy.ThirdParties);
228+
callTreeView.setGroupBySetting(Timeline.TimelineTreeView.AggregatedTimelineTreeView.GroupBy.ThirdParties);
229229
callTreeView.setModelWithEvents(parsedTrace.Renderer.allTraceEntries, parsedTrace);
230230

231231
const tree = callTreeView.buildTree();
@@ -250,7 +250,7 @@ describeWithEnvironment('TimelineTreeView', function() {
250250
const endTime = Trace.Helpers.Timing.microToMilli(parsedTrace.Meta.traceBounds.max);
251251

252252
callTreeView.setRange(startTime, endTime);
253-
callTreeView.setGroupBySettingForTests(Timeline.TimelineTreeView.AggregatedTimelineTreeView.GroupBy.Frame);
253+
callTreeView.setGroupBySetting(Timeline.TimelineTreeView.AggregatedTimelineTreeView.GroupBy.Frame);
254254
callTreeView.setModelWithEvents(parsedTrace.Renderer.allTraceEntries, parsedTrace);
255255

256256
const tree = callTreeView.buildTree();
@@ -269,7 +269,7 @@ describeWithEnvironment('TimelineTreeView', function() {
269269
const endTime = Trace.Helpers.Timing.microToMilli(parsedTrace.Meta.traceBounds.max);
270270

271271
callTreeView.setRange(startTime, endTime);
272-
callTreeView.setGroupBySettingForTests(Timeline.TimelineTreeView.AggregatedTimelineTreeView.GroupBy.URL);
272+
callTreeView.setGroupBySetting(Timeline.TimelineTreeView.AggregatedTimelineTreeView.GroupBy.URL);
273273
callTreeView.setModelWithEvents(parsedTrace.Renderer.allTraceEntries, parsedTrace);
274274

275275
const tree = callTreeView.buildTree();

front_end/panels/timeline/TimelineTreeView.ts

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import * as Platform from '../../core/platform/platform.js';
1010
import * as SDK from '../../core/sdk/sdk.js';
1111
import * as Trace from '../../models/trace/trace.js';
1212
import * as ThirdPartyWeb from '../../third_party/third-party-web/third-party-web.js';
13+
import * as Buttons from '../../ui/components/buttons/buttons.js';
1314
import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
1415
import * as Components from '../../ui/legacy/components/utils/utils.js';
1516
import * as UI from '../../ui/legacy/legacy.js';
@@ -19,7 +20,7 @@ import {ActiveFilters} from './ActiveFilters.js';
1920
import * as Extensions from './extensions/extensions.js';
2021
import {Tracker} from './FreshRecording.js';
2122
import {targetForEvent} from './TargetForEvent.js';
22-
import type {ThirdPartyTreeViewWidget} from './ThirdPartyTreeView.js';
23+
import * as ThirdPartyTreeView from './ThirdPartyTreeView.js';
2324
import {TimelineRegExp} from './TimelineFilters.js';
2425
import {rangeForSelection, type TimelineSelection} from './TimelineSelection.js';
2526
import {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

811854
export 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

front_end/panels/timeline/components/timelineSummary.css

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@
7474
tr .site-column {
7575
border-left: none;
7676
border-bottom: var(--sys-size-1) solid var(--sys-color-divider);
77+
height: auto;
78+
min-height: var(--sys-size-12);
79+
align-content: center;
7780
}
7881

7982
.bottom-filler-td,
@@ -83,10 +86,14 @@
8386

8487
th {
8588
background-color: var(--sys-color-cdt-base-container);
89+
font-weight: var(--ref-typeface-weight-medium);
8690
}
8791

88-
tr.revealed.selected {
89-
background-color: transparent;
92+
tr.revealed:hover {
93+
.button-container {
94+
align-items: center;
95+
display: block;
96+
}
9097
}
9198
}
9299

@@ -127,3 +134,14 @@
127134
overflow: inherit;
128135
max-width: 60%;
129136
}
137+
138+
.hover-bottom-up-button {
139+
display: flex;
140+
flex-direction: row;
141+
justify-content: space-between;
142+
align-items: center;
143+
}
144+
145+
.button-container {
146+
display: none;
147+
}

0 commit comments

Comments
 (0)