chrome-devtools-frontend 1.0.1646714 → 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.
Files changed (42) hide show
  1. package/extension-api/ExtensionAPI.d.ts +26 -0
  2. package/front_end/core/common/Debouncer.ts +9 -1
  3. package/front_end/core/sdk/CSSMatchedStyles.ts +3 -1
  4. package/front_end/core/sdk/CSSMetadata.ts +1 -0
  5. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +65 -0
  6. package/front_end/generated/InspectorBackendCommands.ts +3 -2
  7. package/front_end/generated/SupportedCSSProperties.js +533 -19
  8. package/front_end/generated/protocol.ts +26 -4
  9. package/front_end/models/ai_assistance/agents/AiAgent.ts +8 -1
  10. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +7 -0
  11. package/front_end/models/extensions/ExtensionAPI.ts +47 -21
  12. package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
  13. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +39 -1
  14. package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
  15. package/front_end/panels/ai_assistance/components/AIv2MarkdownRenderer.ts +228 -0
  16. package/front_end/panels/ai_assistance/components/ChatMessage.ts +39 -2
  17. package/front_end/panels/application/ApplicationPanelSidebar.ts +4 -0
  18. package/front_end/panels/application/CookieItemsView.ts +55 -6
  19. package/front_end/panels/application/DOMStorageItemsView.ts +43 -8
  20. package/front_end/panels/application/KeyValueStorageItemsView.ts +17 -9
  21. package/front_end/panels/elements/StylePropertyTreeElement.ts +73 -16
  22. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +9 -1
  23. package/front_end/panels/elements/StylesSidebarPane.ts +22 -8
  24. package/front_end/panels/layer_viewer/PaintProfilerView.ts +309 -182
  25. package/front_end/panels/layer_viewer/paintProfiler.css +27 -23
  26. package/front_end/panels/layers/LayerPaintProfilerView.ts +86 -29
  27. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +210 -356
  28. package/front_end/panels/sources/watchExpressionsSidebarPane.css +7 -0
  29. package/front_end/panels/timeline/TimelinePaintProfilerView.ts +5 -5
  30. package/front_end/panels/timeline/components/NetworkTrackWidget.ts +122 -0
  31. package/front_end/panels/timeline/components/components.ts +2 -0
  32. package/front_end/panels/timeline/components/networkTrackWidget.css +31 -0
  33. package/front_end/panels/timeline/timeline.ts +2 -0
  34. package/front_end/third_party/chromium/README.chromium +1 -1
  35. package/front_end/ui/components/buttons/FloatingButton.ts +17 -2
  36. package/front_end/ui/components/buttons/floatingButton.css +2 -2
  37. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +1 -1
  38. package/front_end/ui/legacy/TextPrompt.ts +24 -18
  39. package/front_end/ui/legacy/Treeoutline.ts +31 -4
  40. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +50 -24
  41. package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
  42. 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 {repeat} = Lit.Directives;
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
- export class PaintProfilerView extends Common.ObjectWrapper.eventMixin<EventTypes, typeof UI.Widget.HBox>(
58
- UI.Widget.HBox) {
59
- private canvasContainer: HTMLElement;
60
- private readonly progressBanner: HTMLElement;
61
- private pieChart: PerfUI.PieChart.PieChart;
62
- private readonly showImageCallback: (arg0?: string|undefined) => void;
63
- private canvas: HTMLCanvasElement;
64
- private context: CanvasRenderingContext2D;
65
- readonly #selectionWindow: PerfUI.OverviewGrid.Window;
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
- private pendingScale: number;
71
- private scale: number;
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
- constructor(showImageCallback: (arg0?: string|undefined) => void) {
80
- super({useShadowDom: true});
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.canvasContainer = this.contentElement.createChild('div', 'paint-profiler-canvas-container');
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.pendingScale = 1;
103
- this.scale = this.pendingScale;
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.update();
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.update();
199
- this.populatePieChart(0, []);
200
- this.#selectionWindow.setResizeEnabled(false);
409
+ this.#isResizeEnabled = false;
410
+ this.#selectionWindow?.setResizeEnabled(false);
411
+ this.requestUpdate();
201
412
  return;
202
413
  }
203
414
 
204
- this.#selectionWindow.setResizeEnabled(true);
205
- this.progressBanner.classList.remove('hidden');
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.progressBanner.classList.add('hidden');
423
+ this.isProfiling = false;
211
424
  this.profiles = profiles;
212
- this.update();
213
- this.updatePieChart();
425
+ this.requestUpdate();
214
426
  }
215
427
 
216
- setScale(scale: number): void {
217
- const needsUpdate = scale > this.scale;
428
+ set scale(scale: number) {
429
+ const needsUpdate = scale > this.#scale;
218
430
  const predictiveGrowthFactor = 2;
219
- this.pendingScale = Math.min(1, scale * predictiveGrowthFactor);
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
- update(): void {
226
- this.canvas.width = this.canvasContainer.clientWidth * window.devicePixelRatio;
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
- private renderBar(index: number, heightByCategory: Record<string, number>): void {
281
- const categories = PaintProfilerView.categories();
282
- let currentHeight = 0;
283
- const x = this.barPaddingWidth + index * this.outerBarWidth;
284
- for (const categoryName in categories) {
285
- if (!heightByCategory[categoryName]) {
286
- continue;
287
- }
288
- currentHeight += heightByCategory[categoryName];
289
- const y = this.canvas.height - currentHeight;
290
- this.context.fillStyle = categories[categoryName].color;
291
- this.context.fillRect(x, y, this.innerBarWidth, heightByCategory[categoryName]);
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.updatePieChart();
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 screenLeft = (this.#selectionWindow.windowLeftRatio || 0) * this.canvas.width;
354
- const screenRight = (this.#selectionWindow.windowRightRatio || 0) * this.canvas.width;
355
- const barLeft = Math.floor(screenLeft / this.outerBarWidth);
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 {left: stepLeft, right: stepRight};
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.pendingScale;
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.scale = scale;
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.reset();
392
- this.#selectionWindow.setResizeEnabled(false);
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]: void;
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
- ${repeat(input.visibleLogItems,
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
- private log: SDK.PaintProfiler.PaintProfilerLogItem[];
496
- private selectionWindow?: {left: number, right: number}|null;
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
- setCommandLog(log: SDK.PaintProfiler.PaintProfilerLogItem[]): void {
513
- this.log = log;
514
- this.updateWindow({left: 0, right: this.log.length});
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
- updateWindow(selectionWindow: {left: number, right: number}|null): void {
518
- this.selectionWindow = selectionWindow;
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.selectionWindow && this.log.length ?
524
- this.log.slice(this.selectionWindow.left, this.selectionWindow.right) :
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);