chrome-devtools-frontend 1.0.1646286 → 1.0.1649421
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SECURITY.md +1 -1
- package/extension-api/ExtensionAPI.d.ts +26 -0
- package/front_end/core/common/Debouncer.ts +9 -1
- package/front_end/core/sdk/CSSMatchedStyles.ts +3 -1
- package/front_end/core/sdk/CSSMetadata.ts +1 -0
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +65 -0
- package/front_end/generated/InspectorBackendCommands.ts +2 -1
- package/front_end/generated/SupportedCSSProperties.js +739 -21
- package/front_end/generated/protocol.ts +26 -0
- package/front_end/models/ai_assistance/AiAgent2.ts +14 -9
- package/front_end/models/ai_assistance/agents/AiAgent.ts +10 -3
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +7 -0
- package/front_end/models/ai_assistance/agents/StorageAgent.ts +45 -0
- package/front_end/models/ai_assistance/ai_assistance.ts +4 -0
- package/front_end/models/ai_assistance/skills/Skill.ts +1 -1
- package/front_end/models/ai_assistance/skills/SkillRegistry.ts +2 -0
- package/front_end/models/ai_assistance/skills/network.md +16 -0
- package/front_end/models/ai_assistance/skills/styling.md +1 -1
- package/front_end/models/ai_assistance/tools/GetNetworkRequestDetails.ts +118 -0
- package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +124 -0
- package/front_end/models/ai_assistance/tools/Tool.ts +2 -0
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +4 -0
- package/front_end/models/emulation/EmulatedDevices.ts +430 -12
- package/front_end/models/extensions/ExtensionAPI.ts +47 -21
- package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +40 -1
- package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
- package/front_end/panels/ai_assistance/components/AIv2MarkdownRenderer.ts +228 -0
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +39 -2
- package/front_end/panels/application/ApplicationPanelSidebar.ts +4 -0
- package/front_end/panels/application/CookieItemsView.ts +55 -6
- package/front_end/panels/application/DOMStorageItemsView.ts +43 -8
- package/front_end/panels/application/KeyValueStorageItemsView.ts +17 -9
- package/front_end/panels/application/WebMCPTreeElement.ts +8 -0
- package/front_end/panels/elements/StylePropertyTreeElement.ts +73 -16
- package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +9 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +22 -8
- package/front_end/panels/layer_viewer/PaintProfilerView.ts +309 -182
- package/front_end/panels/layer_viewer/paintProfiler.css +27 -23
- package/front_end/panels/layers/LayerPaintProfilerView.ts +86 -29
- package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +210 -356
- package/front_end/panels/sources/watchExpressionsSidebarPane.css +7 -0
- package/front_end/panels/timeline/TimelinePaintProfilerView.ts +5 -5
- package/front_end/panels/timeline/components/NetworkTrackWidget.ts +122 -0
- package/front_end/panels/timeline/components/components.ts +2 -0
- package/front_end/panels/timeline/components/networkTrackWidget.css +31 -0
- package/front_end/panels/timeline/timeline.ts +2 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/buttons/FloatingButton.ts +17 -2
- package/front_end/ui/components/buttons/floatingButton.css +2 -2
- package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +1 -1
- package/front_end/ui/legacy/TextPrompt.ts +24 -18
- package/front_end/ui/legacy/Treeoutline.ts +31 -4
- package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +50 -24
- package/front_end/ui/legacy/components/utils/Linkifier.ts +10 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +13 -0
- package/package.json +1 -1
|
@@ -16,7 +16,7 @@ import * as Lit from '../../ui/lit/lit.js';
|
|
|
16
16
|
import paintProfilerStyles from './paintProfiler.css.js';
|
|
17
17
|
|
|
18
18
|
const {html, render, nothing} = Lit;
|
|
19
|
-
const {
|
|
19
|
+
const {ref} = Lit.Directives;
|
|
20
20
|
|
|
21
21
|
const UIStrings = {
|
|
22
22
|
/**
|
|
@@ -54,59 +54,265 @@ let categories: Record<string, PaintProfilerCategory>|null = null;
|
|
|
54
54
|
|
|
55
55
|
let logItemCategoriesMap: Record<string, PaintProfilerCategory>|null = null;
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
function formatPieChartTime(value: number): string {
|
|
58
|
+
return i18n.TimeUtilities.millisToString(value * 1000, true);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface ViewInput {
|
|
62
|
+
isProfiling: boolean;
|
|
63
|
+
log: SDK.PaintProfiler.PaintProfilerLogItem[];
|
|
64
|
+
profiles: Protocol.LayerTree.PaintProfile[]|null|undefined;
|
|
65
|
+
logCategories: PaintProfilerCategory[]|undefined;
|
|
66
|
+
innerBarWidth: number;
|
|
67
|
+
minBarHeight: number;
|
|
68
|
+
barPaddingWidth: number;
|
|
69
|
+
outerBarWidth: number;
|
|
70
|
+
windowLeftRatio: number;
|
|
71
|
+
windowRightRatio: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface ViewOutput {
|
|
75
|
+
onCanvasContainerCreated: (el: Element|undefined) => void;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function calculateSelectionWindow(windowLeftRatio: number, windowRightRatio: number, canvasWidth: number,
|
|
79
|
+
outerBarWidth: number, innerBarWidth: number, barPaddingWidth: number,
|
|
80
|
+
samplesPerBar: number, logLength: number): {left: number, right: number} {
|
|
81
|
+
const screenLeft = windowLeftRatio * canvasWidth;
|
|
82
|
+
const screenRight = windowRightRatio * canvasWidth;
|
|
83
|
+
const barLeft = Math.floor(screenLeft / outerBarWidth);
|
|
84
|
+
const barRight = Math.floor((screenRight + innerBarWidth - barPaddingWidth / 2) / outerBarWidth);
|
|
85
|
+
const stepLeft = Platform.NumberUtilities.clamp(barLeft * samplesPerBar, 0, logLength - 1);
|
|
86
|
+
const stepRight = Platform.NumberUtilities.clamp(barRight * samplesPerBar, 0, logLength);
|
|
87
|
+
|
|
88
|
+
return {left: stepLeft, right: stepRight};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function renderCanvas(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, input: ViewInput,
|
|
92
|
+
samplesPerBar: number): void {
|
|
93
|
+
const profiles = input.profiles;
|
|
94
|
+
const logCategories = input.logCategories;
|
|
95
|
+
if (!profiles || !profiles.length || !logCategories) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let maxBarTime = 0;
|
|
100
|
+
const barTimes = [];
|
|
101
|
+
const barHeightByCategory: Array<Record<string, number>> = [];
|
|
102
|
+
let heightByCategory: Record<string, number> = {};
|
|
103
|
+
for (let i = 0, lastBarIndex = 0, lastBarTime = 0; i < input.log.length;) {
|
|
104
|
+
let categoryName = (logCategories[i]?.name) || 'misc';
|
|
105
|
+
const sampleIndex = input.log[i].commandIndex;
|
|
106
|
+
for (let row = 0; row < profiles.length; row++) {
|
|
107
|
+
const sample = profiles[row][sampleIndex];
|
|
108
|
+
lastBarTime += sample;
|
|
109
|
+
heightByCategory[categoryName] = (heightByCategory[categoryName] || 0) + sample;
|
|
110
|
+
}
|
|
111
|
+
++i;
|
|
112
|
+
if (i - lastBarIndex === samplesPerBar || i === input.log.length) {
|
|
113
|
+
// Normalize by total number of samples accumulated.
|
|
114
|
+
const factor = profiles.length * (i - lastBarIndex);
|
|
115
|
+
lastBarTime /= factor;
|
|
116
|
+
for (categoryName in heightByCategory) {
|
|
117
|
+
heightByCategory[categoryName] /= factor;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
barTimes.push(lastBarTime);
|
|
121
|
+
barHeightByCategory.push(heightByCategory);
|
|
122
|
+
|
|
123
|
+
if (lastBarTime > maxBarTime) {
|
|
124
|
+
maxBarTime = lastBarTime;
|
|
125
|
+
}
|
|
126
|
+
lastBarTime = 0;
|
|
127
|
+
heightByCategory = {};
|
|
128
|
+
lastBarIndex = i;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const paddingHeight = 4 * window.devicePixelRatio;
|
|
133
|
+
const scale = (canvas.height - paddingHeight - input.minBarHeight) / maxBarTime;
|
|
134
|
+
|
|
135
|
+
for (let i = 0; i < barTimes.length; ++i) {
|
|
136
|
+
for (const categoryName in barHeightByCategory[i]) {
|
|
137
|
+
barHeightByCategory[i][categoryName] *= (barTimes[i] * scale + input.minBarHeight) / barTimes[i];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const categories = PaintProfilerView.categories();
|
|
141
|
+
let currentHeight = 0;
|
|
142
|
+
const x = input.barPaddingWidth + i * input.outerBarWidth;
|
|
143
|
+
for (const categoryName in categories) {
|
|
144
|
+
if (!barHeightByCategory[i][categoryName]) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
currentHeight += barHeightByCategory[i][categoryName];
|
|
148
|
+
const y = canvas.height - currentHeight;
|
|
149
|
+
context.fillStyle = categories[categoryName].color;
|
|
150
|
+
context.fillRect(x, y, input.innerBarWidth, barHeightByCategory[i][categoryName]);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function calculatePieChartData(input: ViewInput, canvasWidth: number, samplesPerBar: number,
|
|
156
|
+
emptyPieChartData: PerfUI.PieChart.PieChartData): PerfUI.PieChart.PieChartData {
|
|
157
|
+
const profiles = input.profiles;
|
|
158
|
+
const logCategories = input.logCategories;
|
|
159
|
+
if (!profiles || !profiles.length || !logCategories) {
|
|
160
|
+
return emptyPieChartData;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const {left: stepLeft, right: stepRight} =
|
|
164
|
+
calculateSelectionWindow(input.windowLeftRatio, input.windowRightRatio, canvasWidth, input.outerBarWidth,
|
|
165
|
+
input.innerBarWidth, input.barPaddingWidth, samplesPerBar, input.log.length);
|
|
166
|
+
|
|
167
|
+
let totalTime = 0;
|
|
168
|
+
const timeByCategory: Record<string, number> = {};
|
|
169
|
+
for (let i = stepLeft; i < stepRight; ++i) {
|
|
170
|
+
const logEntry = input.log[i];
|
|
171
|
+
const category = PaintProfilerView.categories()[(logCategories[i]?.name) || 'misc'];
|
|
172
|
+
if (!category) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
timeByCategory[category.color] = timeByCategory[category.color] || 0;
|
|
176
|
+
for (let j = 0; j < profiles.length; ++j) {
|
|
177
|
+
const time = profiles[j][logEntry.commandIndex];
|
|
178
|
+
totalTime += time;
|
|
179
|
+
timeByCategory[category.color] += time;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const slices: PerfUI.PieChart.Slice[] = [];
|
|
183
|
+
for (const color in timeByCategory) {
|
|
184
|
+
slices.push({value: timeByCategory[color] / profiles.length, color, title: ''});
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
...emptyPieChartData,
|
|
188
|
+
total: totalTime / profiles.length,
|
|
189
|
+
slices,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLElement): void => {
|
|
194
|
+
const getTemplate = (pieChartData: PerfUI.PieChart.PieChartData): Lit.LitTemplate => html`
|
|
195
|
+
<style>${paintProfilerStyles}</style>
|
|
196
|
+
<div class="paint-profiler-canvas-container" ${ref(output.onCanvasContainerCreated)}>
|
|
197
|
+
<canvas class="fill"></canvas>
|
|
198
|
+
</div>
|
|
199
|
+
<div class="empty-state ${input.isProfiling ? '' : 'hidden'}">
|
|
200
|
+
${i18nString(UIStrings.profiling)}
|
|
201
|
+
</div>
|
|
202
|
+
<devtools-perf-piechart class="paint-profiler-pie-chart" .data=${pieChartData}></devtools-perf-piechart>
|
|
203
|
+
`;
|
|
204
|
+
|
|
205
|
+
const emptyPieChartData: PerfUI.PieChart.PieChartData = {
|
|
206
|
+
chartName: i18nString(UIStrings.profilingResults),
|
|
207
|
+
size: 55,
|
|
208
|
+
formatter: formatPieChartTime,
|
|
209
|
+
showLegend: false,
|
|
210
|
+
total: 0,
|
|
211
|
+
slices: [],
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
render(getTemplate(emptyPieChartData), target);
|
|
215
|
+
|
|
216
|
+
const canvasContainer = target.querySelector('.paint-profiler-canvas-container');
|
|
217
|
+
const canvas = target.querySelector('canvas');
|
|
218
|
+
if (!canvas || !canvasContainer) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const context = canvas.getContext('2d');
|
|
222
|
+
if (!context) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
canvas.width = canvasContainer.clientWidth * window.devicePixelRatio;
|
|
227
|
+
canvas.height = canvasContainer.clientHeight * window.devicePixelRatio;
|
|
228
|
+
|
|
229
|
+
if (!input.profiles?.length || !input.logCategories) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const maxBars = Math.floor((canvas.width - 2 * input.barPaddingWidth) / input.outerBarWidth);
|
|
234
|
+
const samplesPerBar = Math.ceil(input.log.length / maxBars);
|
|
235
|
+
|
|
236
|
+
renderCanvas(canvas, context, input, samplesPerBar);
|
|
237
|
+
const pieChartData = calculatePieChartData(input, canvas.width, samplesPerBar, emptyPieChartData);
|
|
238
|
+
|
|
239
|
+
render(getTemplate(pieChartData), target);
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
type View = typeof DEFAULT_VIEW;
|
|
243
|
+
|
|
244
|
+
export class PaintProfilerView extends Common.ObjectWrapper.eventMixin<EventTypes, typeof UI.Widget.Widget>(
|
|
245
|
+
UI.Widget.Widget) {
|
|
246
|
+
private canvasContainer?: HTMLElement;
|
|
247
|
+
#selectionWindow?: PerfUI.OverviewGrid.Window;
|
|
66
248
|
private readonly innerBarWidth: number;
|
|
67
249
|
private minBarHeight: number;
|
|
68
250
|
private readonly barPaddingWidth: number;
|
|
69
251
|
private readonly outerBarWidth: number;
|
|
70
|
-
|
|
71
|
-
|
|
252
|
+
#pendingScale: number;
|
|
253
|
+
#scale: number;
|
|
72
254
|
private samplesPerBar: number;
|
|
73
255
|
private log: SDK.PaintProfiler.PaintProfilerLogItem[];
|
|
74
256
|
private snapshot?: SDK.PaintProfiler.PaintProfilerSnapshot|null;
|
|
75
257
|
private logCategories?: PaintProfilerCategory[];
|
|
76
258
|
private profiles?: Protocol.LayerTree.PaintProfile[]|null;
|
|
77
259
|
private updateImageTimer?: number;
|
|
260
|
+
showImageCallback?: (arg0?: string|undefined) => void;
|
|
261
|
+
private isProfiling = false;
|
|
262
|
+
#isResizeEnabled = false;
|
|
78
263
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
this.registerRequiredCSS(paintProfilerStyles);
|
|
264
|
+
readonly #view: View;
|
|
265
|
+
readonly #viewOutput: ViewOutput;
|
|
82
266
|
|
|
267
|
+
constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
|
|
268
|
+
super(element);
|
|
83
269
|
this.contentElement.classList.add('paint-profiler-overview');
|
|
84
|
-
this
|
|
85
|
-
this.progressBanner = this.contentElement.createChild('div', 'empty-state hidden');
|
|
86
|
-
this.progressBanner.textContent = i18nString(UIStrings.profiling);
|
|
87
|
-
this.pieChart = new PerfUI.PieChart.PieChart();
|
|
88
|
-
this.populatePieChart(0, []);
|
|
89
|
-
this.pieChart.classList.add('paint-profiler-pie-chart');
|
|
90
|
-
this.contentElement.appendChild(this.pieChart);
|
|
91
|
-
|
|
92
|
-
this.showImageCallback = showImageCallback;
|
|
93
|
-
this.canvas = this.canvasContainer.createChild('canvas', 'fill');
|
|
94
|
-
this.context = this.canvas.getContext('2d') as CanvasRenderingContext2D;
|
|
95
|
-
this.#selectionWindow = new PerfUI.OverviewGrid.Window(this.canvasContainer);
|
|
96
|
-
this.#selectionWindow.addEventListener(PerfUI.OverviewGrid.Events.WINDOW_CHANGED, this.onWindowChanged, this);
|
|
270
|
+
this.#view = view;
|
|
97
271
|
|
|
98
272
|
this.innerBarWidth = 4 * window.devicePixelRatio;
|
|
99
273
|
this.minBarHeight = window.devicePixelRatio;
|
|
100
274
|
this.barPaddingWidth = 2 * window.devicePixelRatio;
|
|
101
275
|
this.outerBarWidth = this.innerBarWidth + this.barPaddingWidth;
|
|
102
|
-
this
|
|
103
|
-
this
|
|
276
|
+
this.#pendingScale = 1;
|
|
277
|
+
this.#scale = this.#pendingScale;
|
|
104
278
|
this.samplesPerBar = 0;
|
|
105
279
|
this.log = [];
|
|
106
280
|
|
|
281
|
+
this.#viewOutput = {
|
|
282
|
+
onCanvasContainerCreated: (el: Element|undefined) => {
|
|
283
|
+
if (el && !this.canvasContainer) {
|
|
284
|
+
this.canvasContainer = el as HTMLElement;
|
|
285
|
+
this.#selectionWindow = new PerfUI.OverviewGrid.Window(this.canvasContainer);
|
|
286
|
+
this.#selectionWindow.addEventListener(PerfUI.OverviewGrid.Events.WINDOW_CHANGED, this.onWindowChanged, this);
|
|
287
|
+
this.#selectionWindow.setResizeEnabled(this.#isResizeEnabled);
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
|
|
107
292
|
this.reset();
|
|
108
293
|
}
|
|
109
294
|
|
|
295
|
+
set snapshotAndLog(data: {
|
|
296
|
+
snapshot: SDK.PaintProfiler.PaintProfilerSnapshot|null,
|
|
297
|
+
log: SDK.PaintProfiler.PaintProfilerLogItem[],
|
|
298
|
+
clipRect?: Protocol.DOM.Rect|null,
|
|
299
|
+
}|null) {
|
|
300
|
+
const newSnapshot = data ? data.snapshot : null;
|
|
301
|
+
const newLog = data ? data.log : [];
|
|
302
|
+
const newClipRect = data ? (data.clipRect || null) : null;
|
|
303
|
+
if (this.snapshot === newSnapshot && this.log === newLog) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
void this.setSnapshotAndLog(newSnapshot, newLog, newClipRect);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
get snapshoAndLog(): {
|
|
310
|
+
log: SDK.PaintProfiler.PaintProfilerLogItem[],
|
|
311
|
+
snapshot?: SDK.PaintProfiler.PaintProfilerSnapshot|null,
|
|
312
|
+
} {
|
|
313
|
+
return {snapshot: this.snapshot, log: this.log};
|
|
314
|
+
}
|
|
315
|
+
|
|
110
316
|
static categories(): Record<string, PaintProfilerCategory> {
|
|
111
317
|
if (!categories) {
|
|
112
318
|
categories = {
|
|
@@ -179,8 +385,13 @@ export class PaintProfilerView extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
179
385
|
return result;
|
|
180
386
|
}
|
|
181
387
|
|
|
388
|
+
override wasShown(): void {
|
|
389
|
+
super.wasShown();
|
|
390
|
+
this.requestUpdate();
|
|
391
|
+
}
|
|
392
|
+
|
|
182
393
|
override onResize(): void {
|
|
183
|
-
this.
|
|
394
|
+
this.requestUpdate();
|
|
184
395
|
}
|
|
185
396
|
|
|
186
397
|
async setSnapshotAndLog(
|
|
@@ -195,169 +406,75 @@ export class PaintProfilerView extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
195
406
|
this.logCategories = this.log.map(PaintProfilerView.categoryForLogItem);
|
|
196
407
|
|
|
197
408
|
if (!snapshot) {
|
|
198
|
-
this
|
|
199
|
-
this
|
|
200
|
-
this
|
|
409
|
+
this.#isResizeEnabled = false;
|
|
410
|
+
this.#selectionWindow?.setResizeEnabled(false);
|
|
411
|
+
this.requestUpdate();
|
|
201
412
|
return;
|
|
202
413
|
}
|
|
203
414
|
|
|
204
|
-
this.#
|
|
205
|
-
this
|
|
415
|
+
this.#isResizeEnabled = true;
|
|
416
|
+
this.#selectionWindow?.setResizeEnabled(true);
|
|
417
|
+
this.isProfiling = true;
|
|
418
|
+
this.requestUpdate();
|
|
206
419
|
this.updateImage();
|
|
207
420
|
|
|
208
421
|
const profiles = await snapshot.profile(clipRect);
|
|
209
422
|
|
|
210
|
-
this.
|
|
423
|
+
this.isProfiling = false;
|
|
211
424
|
this.profiles = profiles;
|
|
212
|
-
this.
|
|
213
|
-
this.updatePieChart();
|
|
425
|
+
this.requestUpdate();
|
|
214
426
|
}
|
|
215
427
|
|
|
216
|
-
|
|
217
|
-
const needsUpdate = scale > this
|
|
428
|
+
set scale(scale: number) {
|
|
429
|
+
const needsUpdate = scale > this.#scale;
|
|
218
430
|
const predictiveGrowthFactor = 2;
|
|
219
|
-
this
|
|
431
|
+
this.#pendingScale = Math.min(1, scale * predictiveGrowthFactor);
|
|
220
432
|
if (needsUpdate && this.snapshot) {
|
|
221
433
|
this.updateImage();
|
|
222
434
|
}
|
|
223
435
|
}
|
|
224
436
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
this.canvas.height = this.canvasContainer.clientHeight * window.devicePixelRatio;
|
|
228
|
-
this.samplesPerBar = 0;
|
|
229
|
-
if (!this.profiles?.length || !this.logCategories) {
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const maxBars = Math.floor((this.canvas.width - 2 * this.barPaddingWidth) / this.outerBarWidth);
|
|
234
|
-
const sampleCount = this.log.length;
|
|
235
|
-
this.samplesPerBar = Math.ceil(sampleCount / maxBars);
|
|
236
|
-
|
|
237
|
-
let maxBarTime = 0;
|
|
238
|
-
const barTimes = [];
|
|
239
|
-
const barHeightByCategory = [];
|
|
240
|
-
let heightByCategory: Record<string, number> = {};
|
|
241
|
-
for (let i = 0, lastBarIndex = 0, lastBarTime = 0; i < sampleCount;) {
|
|
242
|
-
let categoryName = (this.logCategories[i]?.name) || 'misc';
|
|
243
|
-
const sampleIndex = this.log[i].commandIndex;
|
|
244
|
-
for (let row = 0; row < this.profiles.length; row++) {
|
|
245
|
-
const sample = this.profiles[row][sampleIndex];
|
|
246
|
-
lastBarTime += sample;
|
|
247
|
-
heightByCategory[categoryName] = (heightByCategory[categoryName] || 0) + sample;
|
|
248
|
-
}
|
|
249
|
-
++i;
|
|
250
|
-
if (i - lastBarIndex === this.samplesPerBar || i === sampleCount) {
|
|
251
|
-
// Normalize by total number of samples accumulated.
|
|
252
|
-
const factor = this.profiles.length * (i - lastBarIndex);
|
|
253
|
-
lastBarTime /= factor;
|
|
254
|
-
for (categoryName in heightByCategory) {
|
|
255
|
-
heightByCategory[categoryName] /= factor;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
barTimes.push(lastBarTime);
|
|
259
|
-
barHeightByCategory.push(heightByCategory);
|
|
260
|
-
|
|
261
|
-
if (lastBarTime > maxBarTime) {
|
|
262
|
-
maxBarTime = lastBarTime;
|
|
263
|
-
}
|
|
264
|
-
lastBarTime = 0;
|
|
265
|
-
heightByCategory = {};
|
|
266
|
-
lastBarIndex = i;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const paddingHeight = 4 * window.devicePixelRatio;
|
|
271
|
-
const scale = (this.canvas.height - paddingHeight - this.minBarHeight) / maxBarTime;
|
|
272
|
-
for (let i = 0; i < barTimes.length; ++i) {
|
|
273
|
-
for (const categoryName in barHeightByCategory[i]) {
|
|
274
|
-
barHeightByCategory[i][categoryName] *= (barTimes[i] * scale + this.minBarHeight) / barTimes[i];
|
|
275
|
-
}
|
|
276
|
-
this.renderBar(i, barHeightByCategory[i]);
|
|
277
|
-
}
|
|
437
|
+
get scale(): number {
|
|
438
|
+
return this.#scale;
|
|
278
439
|
}
|
|
279
440
|
|
|
280
|
-
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
this
|
|
291
|
-
this
|
|
292
|
-
}
|
|
441
|
+
override performUpdate(): void {
|
|
442
|
+
const input: ViewInput = {
|
|
443
|
+
isProfiling: this.isProfiling,
|
|
444
|
+
log: this.log,
|
|
445
|
+
profiles: this.profiles,
|
|
446
|
+
logCategories: this.logCategories,
|
|
447
|
+
innerBarWidth: this.innerBarWidth,
|
|
448
|
+
minBarHeight: this.minBarHeight,
|
|
449
|
+
barPaddingWidth: this.barPaddingWidth,
|
|
450
|
+
outerBarWidth: this.outerBarWidth,
|
|
451
|
+
windowLeftRatio: this.#selectionWindow?.windowLeftRatio || 0,
|
|
452
|
+
windowRightRatio: this.#selectionWindow?.windowRightRatio || 0,
|
|
453
|
+
};
|
|
454
|
+
this.#view(input, this.#viewOutput, this.contentElement);
|
|
293
455
|
}
|
|
294
456
|
|
|
295
457
|
private onWindowChanged(): void {
|
|
296
|
-
this.dispatchEventToListeners(Events.WINDOW_CHANGED);
|
|
297
|
-
this.
|
|
458
|
+
this.dispatchEventToListeners(Events.WINDOW_CHANGED, this.selectionWindow());
|
|
459
|
+
this.requestUpdate();
|
|
298
460
|
if (this.updateImageTimer) {
|
|
299
461
|
return;
|
|
300
462
|
}
|
|
301
463
|
this.updateImageTimer = window.setTimeout(this.updateImage.bind(this), 100);
|
|
302
464
|
}
|
|
303
465
|
|
|
304
|
-
private updatePieChart(): void {
|
|
305
|
-
const {total, slices} = this.calculatePieChart();
|
|
306
|
-
this.populatePieChart(total, slices);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
private calculatePieChart(): {total: number, slices: Array<{value: number, color: string, title: string}>} {
|
|
310
|
-
const window = this.selectionWindow();
|
|
311
|
-
if (!this.profiles?.length || !window) {
|
|
312
|
-
return {total: 0, slices: []};
|
|
313
|
-
}
|
|
314
|
-
let totalTime = 0;
|
|
315
|
-
const timeByCategory: Record<string, number> = {};
|
|
316
|
-
for (let i = window.left; i < window.right; ++i) {
|
|
317
|
-
const logEntry = this.log[i];
|
|
318
|
-
const category = PaintProfilerView.categoryForLogItem(logEntry);
|
|
319
|
-
timeByCategory[category.color] = timeByCategory[category.color] || 0;
|
|
320
|
-
for (let j = 0; j < this.profiles.length; ++j) {
|
|
321
|
-
const time = this.profiles[j][logEntry.commandIndex];
|
|
322
|
-
totalTime += time;
|
|
323
|
-
timeByCategory[category.color] += time;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
const slices: PerfUI.PieChart.Slice[] = [];
|
|
327
|
-
for (const color in timeByCategory) {
|
|
328
|
-
slices.push({value: timeByCategory[color] / this.profiles.length, color, title: ''});
|
|
329
|
-
}
|
|
330
|
-
return {total: totalTime / this.profiles.length, slices};
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
private populatePieChart(total: number, slices: PerfUI.PieChart.Slice[]): void {
|
|
334
|
-
this.pieChart.data = {
|
|
335
|
-
chartName: i18nString(UIStrings.profilingResults),
|
|
336
|
-
size: 55,
|
|
337
|
-
formatter: this.formatPieChartTime.bind(this),
|
|
338
|
-
showLegend: false,
|
|
339
|
-
total,
|
|
340
|
-
slices,
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
private formatPieChartTime(value: number): string {
|
|
345
|
-
return i18n.TimeUtilities.millisToString(value * 1000, true);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
466
|
selectionWindow(): {left: number, right: number}|null {
|
|
349
|
-
if (!this.log) {
|
|
467
|
+
if (!this.log || !this.canvasContainer || !this.#selectionWindow) {
|
|
350
468
|
return null;
|
|
351
469
|
}
|
|
352
470
|
|
|
353
|
-
const
|
|
354
|
-
const
|
|
355
|
-
const
|
|
356
|
-
const barRight = Math.floor((screenRight + this.innerBarWidth - this.barPaddingWidth / 2) / this.outerBarWidth);
|
|
357
|
-
const stepLeft = Platform.NumberUtilities.clamp(barLeft * this.samplesPerBar, 0, this.log.length - 1);
|
|
358
|
-
const stepRight = Platform.NumberUtilities.clamp(barRight * this.samplesPerBar, 0, this.log.length);
|
|
471
|
+
const canvasWidth = this.canvasContainer.clientWidth * window.devicePixelRatio;
|
|
472
|
+
const maxBars = Math.floor((canvasWidth - 2 * this.barPaddingWidth) / this.outerBarWidth);
|
|
473
|
+
const samplesPerBar = Math.ceil(this.log.length / maxBars);
|
|
359
474
|
|
|
360
|
-
return
|
|
475
|
+
return calculateSelectionWindow(this.#selectionWindow.windowLeftRatio || 0,
|
|
476
|
+
this.#selectionWindow.windowRightRatio || 0, canvasWidth, this.outerBarWidth,
|
|
477
|
+
this.innerBarWidth, this.barPaddingWidth, samplesPerBar, this.log.length);
|
|
361
478
|
}
|
|
362
479
|
|
|
363
480
|
private updateImage(): void {
|
|
@@ -369,7 +486,7 @@ export class PaintProfilerView extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
369
486
|
left = this.log[window.left].commandIndex;
|
|
370
487
|
right = this.log[window.right - 1].commandIndex;
|
|
371
488
|
}
|
|
372
|
-
const scale = this
|
|
489
|
+
const scale = this.#pendingScale;
|
|
373
490
|
if (!this.snapshot) {
|
|
374
491
|
return;
|
|
375
492
|
}
|
|
@@ -377,8 +494,8 @@ export class PaintProfilerView extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
377
494
|
if (!image) {
|
|
378
495
|
return;
|
|
379
496
|
}
|
|
380
|
-
this
|
|
381
|
-
this.showImageCallback(image);
|
|
497
|
+
this.#scale = scale;
|
|
498
|
+
this.showImageCallback?.(image);
|
|
382
499
|
});
|
|
383
500
|
}
|
|
384
501
|
|
|
@@ -388,8 +505,10 @@ export class PaintProfilerView extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
388
505
|
}
|
|
389
506
|
this.snapshot = null;
|
|
390
507
|
this.profiles = null;
|
|
391
|
-
this.#selectionWindow
|
|
392
|
-
this.#selectionWindow
|
|
508
|
+
this.#selectionWindow?.reset();
|
|
509
|
+
this.#selectionWindow?.setResizeEnabled(false);
|
|
510
|
+
this.#isResizeEnabled = false;
|
|
511
|
+
this.isProfiling = false;
|
|
393
512
|
}
|
|
394
513
|
}
|
|
395
514
|
|
|
@@ -398,7 +517,7 @@ export const enum Events {
|
|
|
398
517
|
}
|
|
399
518
|
|
|
400
519
|
export interface EventTypes {
|
|
401
|
-
[Events.WINDOW_CHANGED]:
|
|
520
|
+
[Events.WINDOW_CHANGED]: {left: number, right: number}|null;
|
|
402
521
|
}
|
|
403
522
|
|
|
404
523
|
export interface CommandLogViewInput {
|
|
@@ -479,9 +598,7 @@ export const COMMAND_LOG_DEFAULT_VIEW = (input: CommandLogViewInput, _output: un
|
|
|
479
598
|
aria-label=${i18nString(UIStrings.commandLog)}
|
|
480
599
|
.template=${html`
|
|
481
600
|
<ul role="tree">
|
|
482
|
-
${
|
|
483
|
-
item => item.commandIndex,
|
|
484
|
-
item => renderLogItem(item))}
|
|
601
|
+
${input.visibleLogItems.map(item => renderLogItem(item))}
|
|
485
602
|
</ul>`}>
|
|
486
603
|
</devtools-tree>
|
|
487
604
|
</div>`,
|
|
@@ -492,16 +609,14 @@ export const COMMAND_LOG_DEFAULT_VIEW = (input: CommandLogViewInput, _output: un
|
|
|
492
609
|
type CommandLogView = typeof COMMAND_LOG_DEFAULT_VIEW;
|
|
493
610
|
|
|
494
611
|
export class PaintProfilerCommandLogView extends UI.Widget.VBox {
|
|
495
|
-
|
|
496
|
-
|
|
612
|
+
#log: SDK.PaintProfiler.PaintProfilerLogItem[] = [];
|
|
613
|
+
#selectionWindow?: {left: number, right: number}|null = null;
|
|
497
614
|
readonly #view: CommandLogView;
|
|
498
615
|
|
|
499
616
|
constructor(element?: HTMLElement, view: CommandLogView = COMMAND_LOG_DEFAULT_VIEW) {
|
|
500
617
|
super(element);
|
|
501
618
|
this.#view = view;
|
|
502
619
|
this.setMinimumSize(100, 25);
|
|
503
|
-
|
|
504
|
-
this.log = [];
|
|
505
620
|
}
|
|
506
621
|
|
|
507
622
|
override wasShown(): void {
|
|
@@ -509,19 +624,31 @@ export class PaintProfilerCommandLogView extends UI.Widget.VBox {
|
|
|
509
624
|
this.requestUpdate();
|
|
510
625
|
}
|
|
511
626
|
|
|
512
|
-
|
|
513
|
-
this
|
|
514
|
-
|
|
627
|
+
set commandLog(log: SDK.PaintProfiler.PaintProfilerLogItem[]) {
|
|
628
|
+
if (this.#log === log) {
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
this.#log = log;
|
|
632
|
+
this.#selectionWindow = {left: 0, right: this.#log.length};
|
|
633
|
+
this.requestUpdate();
|
|
515
634
|
}
|
|
516
635
|
|
|
517
|
-
|
|
518
|
-
this
|
|
636
|
+
get commandLog(): SDK.PaintProfiler.PaintProfilerLogItem[] {
|
|
637
|
+
return this.#log;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
set selectionWindow(window: {left: number, right: number}|null) {
|
|
641
|
+
this.#selectionWindow = window;
|
|
519
642
|
this.requestUpdate();
|
|
520
643
|
}
|
|
521
644
|
|
|
645
|
+
get selectionWindow(): {left: number, right: number}|null {
|
|
646
|
+
return this.#selectionWindow || null;
|
|
647
|
+
}
|
|
648
|
+
|
|
522
649
|
override performUpdate(): Promise<void> {
|
|
523
|
-
const visibleLogItems = this
|
|
524
|
-
this
|
|
650
|
+
const visibleLogItems = this.#selectionWindow && this.#log.length ?
|
|
651
|
+
this.#log.slice(this.#selectionWindow.left, this.#selectionWindow.right) :
|
|
525
652
|
[];
|
|
526
653
|
|
|
527
654
|
this.#view({visibleLogItems}, undefined, this.contentElement);
|