chrome-devtools-frontend 1.0.1519267 → 1.0.1520535

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 (94) hide show
  1. package/config/owner/COMMON_OWNERS +1 -2
  2. package/config/typescript/tsconfig.eslint.json +12 -1
  3. package/docs/ui_engineering.md +1011 -0
  4. package/front_end/core/host/GdpClient.ts +26 -5
  5. package/front_end/core/sdk/NetworkManager.ts +1 -0
  6. package/front_end/core/sdk/NetworkRequest.ts +10 -0
  7. package/front_end/entrypoints/main/MainImpl.ts +6 -1
  8. package/front_end/entrypoints/main/main-meta.ts +3 -3
  9. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +50 -48
  10. package/front_end/models/ai_assistance/agents/PerformanceAnnotationsAgent.ts +4 -4
  11. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +128 -30
  12. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +98 -63
  13. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +317 -640
  14. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +36 -21
  15. package/front_end/models/ai_assistance/performance/AICallTree.snapshot.txt +75 -0
  16. package/front_end/models/ai_assistance/performance/AICallTree.ts +14 -6
  17. package/front_end/models/ai_assistance/performance/AIContext.ts +62 -7
  18. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +5 -5
  19. package/front_end/models/badges/Badge.ts +6 -1
  20. package/front_end/models/badges/StarterBadge.ts +5 -0
  21. package/front_end/models/badges/UserBadges.ts +5 -4
  22. package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
  23. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +14 -7
  24. package/front_end/panels/ai_assistance/PatchWidget.ts +17 -55
  25. package/front_end/panels/ai_assistance/components/ChatView.ts +45 -69
  26. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +47 -1
  27. package/front_end/panels/ai_assistance/components/chatView.css +13 -1
  28. package/front_end/panels/animation/AnimationTimeline.ts +1 -1
  29. package/front_end/panels/animation/animationTimeline.css +4 -0
  30. package/front_end/panels/application/preloading/components/PreloadingString.ts +2 -5
  31. package/front_end/panels/common/AiCodeCompletionTeaser.ts +5 -0
  32. package/front_end/panels/common/aiCodeCompletionTeaser.css +6 -1
  33. package/front_end/panels/console/ConsolePrompt.ts +6 -0
  34. package/front_end/panels/console/ConsoleView.ts +4 -2
  35. package/front_end/panels/coverage/CoverageListView.ts +146 -198
  36. package/front_end/panels/coverage/CoverageView.ts +48 -18
  37. package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +2 -0
  38. package/front_end/panels/network/NetworkDataGridNode.ts +22 -0
  39. package/front_end/panels/network/NetworkLogViewColumns.ts +9 -0
  40. package/front_end/panels/recorder/components/CreateRecordingView.ts +2 -0
  41. package/front_end/panels/search/SearchResultsPane.ts +48 -15
  42. package/front_end/panels/search/SearchView.ts +33 -30
  43. package/front_end/panels/search/searchView.css +0 -2
  44. package/front_end/panels/settings/components/SyncSection.ts +4 -4
  45. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +1 -4
  46. package/front_end/panels/sources/DebuggerPlugin.ts +4 -0
  47. package/front_end/panels/timeline/ThirdPartyTreeView.ts +1 -1
  48. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +0 -8
  49. package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -5
  50. package/front_end/panels/timeline/TimelinePanel.ts +2 -0
  51. package/front_end/panels/timeline/TimelineTreeView.ts +1 -1
  52. package/front_end/panels/timeline/components/ExportTraceOptions.ts +56 -4
  53. package/front_end/panels/timeline/components/exportTraceOptions.css +5 -0
  54. package/front_end/third_party/chromium/README.chromium +1 -1
  55. package/front_end/third_party/lighthouse/README.chromium +8 -1
  56. package/front_end/third_party/puppeteer/README.chromium +2 -2
  57. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
  58. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
  59. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
  60. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  61. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js +1 -1
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js.map +1 -1
  63. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.d.ts.map +1 -1
  64. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js +15 -16
  65. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js.map +1 -1
  66. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  67. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  68. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js.map +1 -1
  70. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  71. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +4 -4
  72. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
  73. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
  74. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js +1 -1
  75. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js.map +1 -1
  76. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.d.ts.map +1 -1
  77. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js +15 -16
  78. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js.map +1 -1
  79. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  80. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  81. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js +1 -1
  82. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js.map +1 -1
  83. package/front_end/third_party/puppeteer/package/package.json +3 -2
  84. package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
  85. package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +1 -1
  86. package/front_end/third_party/puppeteer/package/src/node/PipeTransport.ts +15 -17
  87. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  88. package/front_end/third_party/puppeteer/package/src/util/Function.ts +1 -1
  89. package/front_end/tsconfig.json +12 -1
  90. package/front_end/ui/legacy/InspectorView.ts +86 -13
  91. package/front_end/ui/legacy/TabbedPane.ts +2 -1
  92. package/front_end/ui/visual_logging/KnownContextValues.ts +6 -0
  93. package/front_end/ui/visual_logging/LoggingEvents.ts +1 -1
  94. package/package.json +1 -1
@@ -2,6 +2,7 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
  /* eslint-disable rulesdir/no-imperative-dom-api */
5
+ /* eslint-disable rulesdir/no-lit-render-outside-of-view */
5
6
 
6
7
  import * as Common from '../../core/common/common.js';
7
8
  import * as i18n from '../../core/i18n/i18n.js';
@@ -10,13 +11,23 @@ import * as TextUtils from '../../models/text_utils/text_utils.js';
10
11
  import * as Workspace from '../../models/workspace/workspace.js';
11
12
  import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
12
13
  import * as UI from '../../ui/legacy/legacy.js';
14
+ import {Directives, html, nothing, render} from '../../ui/lit/lit.js';
13
15
 
14
16
  import coverageListViewStyles from './coverageListView.css.js';
15
- import {
16
- CoverageType,
17
- SourceURLCoverageInfo,
18
- type URLCoverageInfo,
19
- } from './CoverageModel.js';
17
+ import {CoverageType} from './CoverageModel.js';
18
+
19
+ export interface CoverageListItem {
20
+ url: Platform.DevToolsPath.UrlString;
21
+ type: CoverageType;
22
+ size: number;
23
+ usedSize: number;
24
+ unusedSize: number;
25
+ usedPercentage: number;
26
+ unusedPercentage: number;
27
+ sources: CoverageListItem[];
28
+ isContentScript: boolean;
29
+ generatedUrl?: Platform.DevToolsPath.UrlString;
30
+ }
20
31
 
21
32
  const UIStrings = {
22
33
  /**
@@ -113,6 +124,7 @@ const UIStrings = {
113
124
  } as const;
114
125
  const str_ = i18n.i18n.registerUIStrings('panels/coverage/CoverageListView.ts', UIStrings);
115
126
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
127
+ const {styleMap} = Directives;
116
128
 
117
129
  export function coverageTypeToString(type: CoverageType): string {
118
130
  const types = [];
@@ -128,16 +140,14 @@ export function coverageTypeToString(type: CoverageType): string {
128
140
  }
129
141
 
130
142
  export class CoverageListView extends UI.Widget.VBox {
131
- private readonly nodeForCoverageInfo: Map<URLCoverageInfo, GridNode>;
132
- private readonly isVisibleFilter: (arg0: URLCoverageInfo) => boolean;
143
+ private readonly nodeForUrl: Map<Platform.DevToolsPath.UrlString, GridNode>;
133
144
  private highlightRegExp: RegExp|null;
134
145
  private dataGrid: DataGrid.SortableDataGrid.SortableDataGrid<GridNode>;
135
146
 
136
- constructor(isVisibleFilter: (arg0: URLCoverageInfo) => boolean) {
147
+ constructor() {
137
148
  super({useShadowDom: true});
138
149
  this.registerRequiredCSS(coverageListViewStyles);
139
- this.nodeForCoverageInfo = new Map();
140
- this.isVisibleFilter = isVisibleFilter;
150
+ this.nodeForUrl = new Map();
141
151
  this.highlightRegExp = null;
142
152
 
143
153
  const columns = [
@@ -179,113 +189,82 @@ export class CoverageListView extends UI.Widget.VBox {
179
189
  weight: 1,
180
190
  },
181
191
  ] as DataGrid.DataGrid.ColumnDescriptor[];
182
- this.dataGrid = new DataGrid.SortableDataGrid.SortableDataGrid<GridNode>({
183
- displayName: i18nString(UIStrings.codeCoverage),
184
- columns,
185
- refreshCallback: undefined,
186
- deleteCallback: undefined,
187
- });
192
+ this.dataGrid =
193
+ DataGrid.SortableDataGrid.SortableDataGrid.create(['dummy'], [], i18nString(UIStrings.codeCoverage)) as
194
+ DataGrid.SortableDataGrid.SortableDataGrid<GridNode>;
195
+ this.dataGrid.removeColumn('dummy');
196
+ for (const column of columns) {
197
+ this.dataGrid.addColumn(column);
198
+ }
199
+ this.dataGrid.setColumnsVisibility(new Set(columns.map(column => column.id)));
188
200
  this.dataGrid.setResizeMethod(DataGrid.DataGrid.ResizeMethod.LAST);
189
201
  this.dataGrid.setStriped(true);
190
202
  this.dataGrid.element.classList.add('flex-auto');
191
203
  this.dataGrid.addEventListener(DataGrid.DataGrid.Events.OPENED_NODE, this.onOpenedNode, this);
192
- this.dataGrid.addEventListener(DataGrid.DataGrid.Events.SORTING_CHANGED, this.sortingChanged, this);
193
204
 
194
205
  const dataGridWidget = this.dataGrid.asWidget();
195
206
  dataGridWidget.show(this.contentElement);
196
207
  this.setDefaultFocusedChild(dataGridWidget);
197
208
  }
198
209
 
199
- update(coverageInfo: URLCoverageInfo[] = []): void {
210
+ update(coverageInfo: CoverageListItem[], highlightRegExp: RegExp|null): void {
211
+ this.highlightRegExp = highlightRegExp;
212
+ const maxSize = coverageInfo.reduce((acc, entry) => Math.max(acc, entry.size), 0);
213
+
214
+ const coverageUrls = new Set(coverageInfo.map(info => info.url));
215
+ for (const [url, node] of this.nodeForUrl.entries()) {
216
+ if (!coverageUrls.has(url)) {
217
+ node.remove();
218
+ this.nodeForUrl.delete(url);
219
+ }
220
+ }
221
+
200
222
  let hadUpdates = false;
201
- const maxSize = coverageInfo.reduce((acc, entry) => Math.max(acc, entry.size()), 0);
202
- const rootNode = this.dataGrid.rootNode();
203
223
  for (const entry of coverageInfo) {
204
- let node = this.nodeForCoverageInfo.get(entry);
224
+ let node = this.nodeForUrl.get(entry.url);
205
225
  if (node) {
206
- if (this.isVisibleFilter(node.coverageInfo)) {
207
- hadUpdates = node.refreshIfNeeded(maxSize) || hadUpdates;
208
- if (entry.sourcesURLCoverageInfo.size > 0) {
209
- this.updateSourceNodes(entry.sourcesURLCoverageInfo, maxSize, node);
210
- }
226
+ hadUpdates = node.refreshIfNeeded(maxSize, entry) || hadUpdates;
227
+ if (entry.sources.length > 0) {
228
+ this.updateSourceNodes(entry.sources, maxSize, node);
211
229
  }
230
+ node.setHighlight(this.highlightRegExp);
212
231
  continue;
213
232
  }
214
233
  node = new GridNode(entry, maxSize);
215
- this.nodeForCoverageInfo.set(entry, node);
216
- if (this.isVisibleFilter(node.coverageInfo)) {
217
- rootNode.appendChild(node);
218
- if (entry.sourcesURLCoverageInfo.size > 0) {
219
- void this.createSourceNodes(entry.sourcesURLCoverageInfo, maxSize, node);
220
- }
221
- hadUpdates = true;
234
+ this.nodeForUrl.set(entry.url, node);
235
+ this.appendNodeByType(node);
236
+ if (entry.sources.length > 0) {
237
+ this.updateSourceNodes(entry.sources, maxSize, node);
222
238
  }
239
+ node.setHighlight(this.highlightRegExp);
240
+ hadUpdates = true;
223
241
  }
224
242
  if (hadUpdates) {
225
- this.sortingChanged();
243
+ this.dataGrid.dispatchEventToListeners(DataGrid.DataGrid.Events.SORTING_CHANGED);
226
244
  }
227
245
  }
228
246
 
229
- updateSourceNodes(
230
- sourcesURLCoverageInfo: Map<Platform.DevToolsPath.UrlString, SourceURLCoverageInfo>, maxSize: number,
231
- node: GridNode): void {
232
- let shouldCreateSourceNodes = false;
233
- for (const coverageInfo of sourcesURLCoverageInfo.values()) {
234
- const sourceNode = this.nodeForCoverageInfo.get(coverageInfo);
247
+ updateSourceNodes(sources: CoverageListItem[], maxSize: number, node: GridNode): void {
248
+ for (const coverageInfo of sources) {
249
+ const sourceNode = this.nodeForUrl.get(coverageInfo.url);
235
250
  if (sourceNode) {
236
- sourceNode.refreshIfNeeded(maxSize);
251
+ sourceNode.refreshIfNeeded(maxSize, coverageInfo);
237
252
  } else {
238
- shouldCreateSourceNodes = true;
239
- break;
253
+ const sourceNode = new GridNode(coverageInfo, maxSize);
254
+ node.appendChild(sourceNode);
255
+ this.nodeForUrl.set(coverageInfo.url, sourceNode);
240
256
  }
241
257
  }
242
- if (shouldCreateSourceNodes) {
243
- void this.createSourceNodes(sourcesURLCoverageInfo, maxSize, node);
244
- }
245
- }
246
-
247
- async createSourceNodes(
248
- sourcesURLCoverageInfo: Map<Platform.DevToolsPath.UrlString, SourceURLCoverageInfo>, maxSize: number,
249
- node: GridNode): Promise<void> {
250
- for (const coverageInfo of sourcesURLCoverageInfo.values()) {
251
- const sourceNode = new GridNode(coverageInfo, maxSize);
252
- node.appendChild(sourceNode);
253
- this.nodeForCoverageInfo.set(coverageInfo, sourceNode);
254
- }
255
258
  }
256
259
 
257
260
  reset(): void {
258
- this.nodeForCoverageInfo.clear();
261
+ this.nodeForUrl.clear();
259
262
  this.dataGrid.rootNode().removeChildren();
260
263
  }
261
264
 
262
- updateFilterAndHighlight(highlightRegExp: RegExp|null): void {
263
- this.highlightRegExp = highlightRegExp;
264
- let hadTreeUpdates = false;
265
- for (const node of this.nodeForCoverageInfo.values()) {
266
- const shouldBeVisible = this.isVisibleFilter(node.coverageInfo);
267
- const isVisible = Boolean(node.parent);
268
- if (shouldBeVisible) {
269
- node.setHighlight(this.highlightRegExp);
270
- }
271
- if (shouldBeVisible === isVisible) {
272
- continue;
273
- }
274
- hadTreeUpdates = true;
275
- if (!shouldBeVisible) {
276
- node.remove();
277
- } else {
278
- this.appendNodeByType(node);
279
- }
280
- }
281
- if (hadTreeUpdates) {
282
- this.sortingChanged();
283
- }
284
- }
285
-
286
265
  private appendNodeByType(node: GridNode): void {
287
- if (node.coverageInfo instanceof SourceURLCoverageInfo) {
288
- const parentNode = this.nodeForCoverageInfo.get(node.coverageInfo.generatedURLCoverageInfo);
266
+ if (node.coverageInfo.generatedUrl) {
267
+ const parentNode = this.nodeForUrl.get(node.coverageInfo.generatedUrl);
289
268
  parentNode?.appendChild(node);
290
269
  } else {
291
270
  this.dataGrid.rootNode().appendChild(node);
@@ -293,11 +272,9 @@ export class CoverageListView extends UI.Widget.VBox {
293
272
  }
294
273
 
295
274
  selectByUrl(url: string): void {
296
- for (const [info, node] of this.nodeForCoverageInfo.entries()) {
297
- if (info.url() === url) {
298
- node.revealAndSelect();
299
- break;
300
- }
275
+ const node = this.nodeForUrl.get(url as Platform.DevToolsPath.UrlString);
276
+ if (node) {
277
+ node.revealAndSelect();
301
278
  }
302
279
  }
303
280
 
@@ -311,7 +288,7 @@ export class CoverageListView extends UI.Widget.VBox {
311
288
  return;
312
289
  }
313
290
  const coverageInfo = (node as GridNode).coverageInfo;
314
- const sourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(coverageInfo.url());
291
+ const sourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(coverageInfo.url);
315
292
  if (!sourceCode) {
316
293
  return;
317
294
  }
@@ -321,21 +298,6 @@ export class CoverageListView extends UI.Widget.VBox {
321
298
  }
322
299
  void Common.Revealer.reveal(sourceCode);
323
300
  }
324
-
325
- private sortingChanged(): void {
326
- const columnId = this.dataGrid.sortColumnId();
327
- if (!columnId) {
328
- return;
329
- }
330
- const sortFunction = GridNode.sortFunctionForColumn(columnId) as (
331
- (arg0: DataGrid.SortableDataGrid.SortableDataGridNode<GridNode>,
332
- arg1: DataGrid.SortableDataGrid.SortableDataGridNode<GridNode>) => number) |
333
- null;
334
- if (!sortFunction) {
335
- return;
336
- }
337
- this.dataGrid.sortNodes(sortFunction, !this.dataGrid.isSortOrderAscending());
338
- }
339
301
  }
340
302
 
341
303
  let percentageFormatter: Intl.NumberFormat|null = null;
@@ -360,18 +322,28 @@ function getBytesFormatter(): Intl.NumberFormat {
360
322
  }
361
323
 
362
324
  export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<GridNode> {
363
- coverageInfo: URLCoverageInfo;
325
+ coverageInfo: CoverageListItem;
364
326
  private lastUsedSize!: number|undefined;
365
327
  private url: Platform.DevToolsPath.UrlString;
366
328
  private maxSize: number;
367
329
  private highlightRegExp: RegExp|null;
368
330
 
369
- constructor(coverageInfo: URLCoverageInfo, maxSize: number) {
331
+ constructor(coverageInfo: CoverageListItem, maxSize: number) {
370
332
  super();
371
333
  this.coverageInfo = coverageInfo;
372
- this.url = coverageInfo.url();
334
+ this.url = coverageInfo.url;
373
335
  this.maxSize = maxSize;
374
336
  this.highlightRegExp = null;
337
+ this.#updateData(coverageInfo);
338
+ }
339
+
340
+ #updateData(coverageInfo: CoverageListItem): void {
341
+ this.data['url'] = this.url;
342
+ this.data['type'] = coverageTypeToString(coverageInfo.type);
343
+ this.data['size'] = coverageInfo.size;
344
+ this.data['unused-size'] = coverageInfo.unusedSize;
345
+ this.data['bars'] = coverageInfo.unusedSize;
346
+ this.coverageInfo = coverageInfo;
375
347
  }
376
348
 
377
349
  setHighlight(highlightRegExp: RegExp|null): void {
@@ -379,105 +351,104 @@ export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<Gri
379
351
  return;
380
352
  }
381
353
  this.highlightRegExp = highlightRegExp;
354
+ for (const child of this.children) {
355
+ (child as GridNode).setHighlight(this.highlightRegExp);
356
+ }
382
357
  this.refresh();
383
358
  }
384
359
 
385
- refreshIfNeeded(maxSize: number): boolean {
386
- if (this.lastUsedSize === this.coverageInfo.usedSize() && maxSize === this.maxSize) {
360
+ refreshIfNeeded(maxSize: number, coverageInfo: CoverageListItem): boolean {
361
+ if (this.lastUsedSize === coverageInfo.usedSize && maxSize === this.maxSize) {
387
362
  return false;
388
363
  }
389
- this.lastUsedSize = this.coverageInfo.usedSize();
364
+ this.lastUsedSize = coverageInfo.usedSize;
390
365
  this.maxSize = maxSize;
391
366
  this.refresh();
367
+ this.#updateData(coverageInfo);
392
368
  return true;
393
369
  }
394
370
 
395
371
  override createCell(columnId: string): HTMLElement {
396
372
  const cell = this.createTD(columnId);
373
+ const info = this.coverageInfo;
374
+ const formatBytes = (value: number|undefined): string => {
375
+ return getBytesFormatter().format(value ?? 0);
376
+ };
377
+ const formatPercent = (value: number|undefined): string => {
378
+ return getPercentageFormatter().format(value ?? 0);
379
+ };
397
380
  switch (columnId) {
398
381
  case 'url': {
399
382
  UI.Tooltip.Tooltip.install(cell, this.url);
400
- const outer = cell.createChild('div', 'url-outer');
401
- const prefix = outer.createChild('div', 'url-prefix');
402
- const suffix = outer.createChild('div', 'url-suffix');
383
+ this.setCellAccessibleName(this.url, cell, columnId);
403
384
  const splitURL = /^(.*)(\/[^/]*)$/.exec(this.url);
404
- prefix.textContent = splitURL ? splitURL[1] : this.url;
405
- suffix.textContent = splitURL ? splitURL[2] : '';
385
+ render(
386
+ html`
387
+ <div class="url-outer">
388
+ <div class="url-prefix">${splitURL ? splitURL[1] : this.url}</div>
389
+ <div class="url-suffix">${splitURL ? splitURL[2] : ''}</div>
390
+ </div>`,
391
+ cell);
406
392
  if (this.highlightRegExp) {
407
- this.highlight(outer, this.url);
393
+ this.highlight(cell, this.url);
408
394
  }
409
- this.setCellAccessibleName(this.url, cell, columnId);
410
395
  break;
411
396
  }
412
397
  case 'type': {
413
- cell.textContent = coverageTypeToString(this.coverageInfo.type());
414
- if (this.coverageInfo.type() & CoverageType.JAVA_SCRIPT_PER_FUNCTION) {
415
- UI.Tooltip.Tooltip.install(cell, i18nString(UIStrings.jsCoverageWithPerFunction));
416
- } else if (this.coverageInfo.type() & CoverageType.JAVA_SCRIPT) {
417
- UI.Tooltip.Tooltip.install(cell, i18nString(UIStrings.jsCoverageWithPerBlock));
418
- }
398
+ UI.Tooltip.Tooltip.install(
399
+ cell,
400
+ info.type & CoverageType.JAVA_SCRIPT_PER_FUNCTION ? i18nString(UIStrings.jsCoverageWithPerFunction) :
401
+ info.type & CoverageType.JAVA_SCRIPT ? i18nString(UIStrings.jsCoverageWithPerBlock) :
402
+ '');
403
+ render(coverageTypeToString(this.coverageInfo.type), cell);
419
404
  break;
420
405
  }
421
406
  case 'size': {
422
- const size = this.coverageInfo.size() || 0;
423
- const sizeSpan = cell.createChild('span');
424
- const sizeFormatted = getBytesFormatter().format(size);
425
- sizeSpan.textContent = sizeFormatted;
426
- const sizeAccessibleName = i18nString(UIStrings.sBytes, {n: size});
427
- this.setCellAccessibleName(sizeAccessibleName, cell, columnId);
407
+ this.setCellAccessibleName(i18nString(UIStrings.sBytes, {n: info.size || 0}), cell, columnId);
408
+ render(html`<span>${formatBytes(info.size)}</span>`, cell);
428
409
  break;
429
410
  }
430
411
  case 'unused-size': {
431
- const unusedSize = this.coverageInfo.unusedSize() || 0;
432
- const unusedSizeSpan = cell.createChild('span');
433
- const unusedPercentsSpan = cell.createChild('span', 'percent-value');
434
- const unusedSizeFormatted = getBytesFormatter().format(unusedSize);
435
- unusedSizeSpan.textContent = unusedSizeFormatted;
436
- const unusedPercentFormatted = getPercentageFormatter().format(this.coverageInfo.unusedPercentage());
437
- unusedPercentsSpan.textContent = unusedPercentFormatted;
438
- const unusedAccessibleName = i18nString(UIStrings.sBytesS, {n: unusedSize, percentage: unusedPercentFormatted});
439
- this.setCellAccessibleName(unusedAccessibleName, cell, columnId);
412
+ this.setCellAccessibleName(
413
+ i18nString(UIStrings.sBytesS, {n: info.unusedSize, percentage: formatPercent(info.unusedPercentage)}), cell,
414
+ columnId);
415
+ // clang-format off
416
+ render(html`
417
+ <span>${formatBytes(info.unusedSize)}</span>
418
+ <span class="percent-value">
419
+ ${formatPercent(info.unusedPercentage)}
420
+ </span>`, cell);
421
+ // clang-format on
440
422
  break;
441
423
  }
442
424
  case 'bars': {
443
- const barContainer = cell.createChild('div', 'bar-container');
444
- const unusedPercent = getPercentageFormatter().format(this.coverageInfo.unusedPercentage());
445
- const usedPercent = getPercentageFormatter().format(this.coverageInfo.usedPercentage());
446
- if (this.coverageInfo.unusedSize() > 0) {
447
- const unusedSizeBar = barContainer.createChild('div', 'bar bar-unused-size');
448
- unusedSizeBar.style.width = ((this.coverageInfo.unusedSize() / this.maxSize) * 100 || 0) + '%';
449
- if (this.coverageInfo.type() & CoverageType.JAVA_SCRIPT_PER_FUNCTION) {
450
- UI.Tooltip.Tooltip.install(
451
- unusedSizeBar,
452
- i18nString(
453
- UIStrings.sBytesSBelongToFunctionsThatHave,
454
- {PH1: this.coverageInfo.unusedSize(), PH2: unusedPercent}));
455
- } else if (this.coverageInfo.type() & CoverageType.JAVA_SCRIPT) {
456
- UI.Tooltip.Tooltip.install(
457
- unusedSizeBar,
458
- i18nString(
459
- UIStrings.sBytesSBelongToBlocksOf, {PH1: this.coverageInfo.unusedSize(), PH2: unusedPercent}));
460
- }
461
- }
462
- if (this.coverageInfo.usedSize() > 0) {
463
- const usedSizeBar = barContainer.createChild('div', 'bar bar-used-size');
464
- usedSizeBar.style.width = ((this.coverageInfo.usedSize() / this.maxSize) * 100 || 0) + '%';
465
- if (this.coverageInfo.type() & CoverageType.JAVA_SCRIPT_PER_FUNCTION) {
466
- UI.Tooltip.Tooltip.install(
467
- usedSizeBar,
468
- i18nString(
469
- UIStrings.sBytesSBelongToFunctionsThatHaveExecuted,
470
- {PH1: this.coverageInfo.usedSize(), PH2: usedPercent}));
471
- } else if (this.coverageInfo.type() & CoverageType.JAVA_SCRIPT) {
472
- UI.Tooltip.Tooltip.install(
473
- usedSizeBar,
474
- i18nString(
475
- UIStrings.sBytesSBelongToBlocksOfJavascript,
476
- {PH1: this.coverageInfo.usedSize(), PH2: usedPercent}));
477
- }
478
- }
479
425
  this.setCellAccessibleName(
480
- i18nString(UIStrings.sOfFileUnusedSOfFileUsed, {PH1: unusedPercent, PH2: usedPercent}), cell, columnId);
426
+ i18nString(
427
+ UIStrings.sOfFileUnusedSOfFileUsed,
428
+ {PH1: formatPercent(info.unusedPercentage), PH2: formatPercent(info.usedPercentage)}),
429
+ cell, columnId);
430
+ // clang-format off
431
+ render(html`
432
+ <div class="bar-container">
433
+ ${info.unusedSize > 0 ? html`
434
+ <div class="bar bar-unused-size"
435
+ title=${
436
+ info.type & CoverageType.JAVA_SCRIPT_PER_FUNCTION ? i18nString(UIStrings.sBytesSBelongToFunctionsThatHave, {PH1: info.unusedSize, PH2: formatPercent(info.unusedPercentage)}) :
437
+ info.type & CoverageType.JAVA_SCRIPT ? i18nString(UIStrings.sBytesSBelongToBlocksOf, {PH1: info.unusedSize, PH2: formatPercent(info.unusedPercentage)}) :
438
+ ''}
439
+ style=${styleMap({width: ((info.unusedSize / this.maxSize) * 100 || 0) + '%'})}>
440
+ </div>` : nothing}
441
+ ${info.usedSize > 0 ? html`
442
+ <div class="bar bar-used-size"
443
+ title=${
444
+ info.type & CoverageType.JAVA_SCRIPT_PER_FUNCTION ? i18nString(UIStrings.sBytesSBelongToFunctionsThatHaveExecuted, {PH1: info.usedSize, PH2: formatPercent(info.usedPercentage)}) :
445
+ info.type & CoverageType.JAVA_SCRIPT ? i18nString(UIStrings.sBytesSBelongToBlocksOfJavascript, {PH1: info.usedSize, PH2: formatPercent(info.usedPercentage)}) :
446
+ ''}
447
+ { PH1: info.usedSize, PH2: formatPercent(info.usedPercentage) })}
448
+ style=${styleMap({width:((info.usedSize / this.maxSize) * 100 || 0) + '%'})}>
449
+ </div>` : nothing}
450
+ </div>`, cell);
451
+ // clang-format on
481
452
  }
482
453
  }
483
454
  return cell;
@@ -494,27 +465,4 @@ export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<Gri
494
465
  const range = new TextUtils.TextRange.SourceRange(matches.index, matches[0].length);
495
466
  UI.UIUtils.highlightRangesWithStyleClass(element, [range], 'filter-highlight');
496
467
  }
497
-
498
- static sortFunctionForColumn(columnId: string): ((arg0: GridNode, arg1: GridNode) => number)|null {
499
- const compareURL = (a: GridNode, b: GridNode): number => a.url.localeCompare(b.url);
500
- switch (columnId) {
501
- case 'url':
502
- return compareURL;
503
- case 'type':
504
- return (a: GridNode, b: GridNode) => {
505
- const typeA = coverageTypeToString(a.coverageInfo.type());
506
- const typeB = coverageTypeToString(b.coverageInfo.type());
507
- return typeA.localeCompare(typeB) || compareURL(a, b);
508
- };
509
- case 'size':
510
- return (a: GridNode, b: GridNode) => a.coverageInfo.size() - b.coverageInfo.size() || compareURL(a, b);
511
- case 'bars':
512
- case 'unused-size':
513
- return (a: GridNode, b: GridNode) =>
514
- a.coverageInfo.unusedSize() - b.coverageInfo.unusedSize() || compareURL(a, b);
515
- default:
516
- console.assert(false, 'Unknown sort field: ' + columnId);
517
- return null;
518
- }
519
- }
520
468
  }
@@ -17,8 +17,15 @@ import * as UI from '../../ui/legacy/legacy.js';
17
17
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
18
18
 
19
19
  import {CoverageDecorationManager} from './CoverageDecorationManager.js';
20
- import {CoverageListView} from './CoverageListView.js';
21
- import {type CoverageInfo, CoverageModel, CoverageType, Events, type URLCoverageInfo} from './CoverageModel.js';
20
+ import {type CoverageListItem, CoverageListView} from './CoverageListView.js';
21
+ import {
22
+ type CoverageInfo,
23
+ CoverageModel,
24
+ CoverageType,
25
+ Events,
26
+ SourceURLCoverageInfo,
27
+ type URLCoverageInfo,
28
+ } from './CoverageModel.js';
22
29
  import coverageViewStyles from './coverageView.css.js';
23
30
 
24
31
  const UIStrings = {
@@ -254,7 +261,7 @@ export class CoverageView extends UI.Widget.VBox {
254
261
  this.bfcacheReloadPromptPage = this.buildReloadPromptPage(i18nString(UIStrings.bfcacheNoCapture), 'bfcache-page');
255
262
  this.activationReloadPromptPage =
256
263
  this.buildReloadPromptPage(i18nString(UIStrings.activationNoCapture), 'prerender-page');
257
- this.listView = new CoverageListView(this.isVisible.bind(this, false));
264
+ this.listView = new CoverageListView();
258
265
 
259
266
  this.statusToolbarElement = this.contentElement.createChild('div', 'coverage-toolbar-summary');
260
267
  this.statusMessageElement = this.statusToolbarElement.createChild('div', 'coverage-message');
@@ -435,7 +442,29 @@ export class CoverageView extends UI.Widget.VBox {
435
442
  }
436
443
 
437
444
  private updateListView(): void {
438
- this.listView.update(this.model?.entries() || []);
445
+ const entries =
446
+ (this.model?.entries() || [])
447
+ .map(entry => this.toCoverageListItem(entry))
448
+ .filter(info => this.isVisible(info))
449
+ .map(
450
+ (entry: CoverageListItem) =>
451
+ ({...entry, sources: entry.sources.filter((entry: CoverageListItem) => this.isVisible(entry))}));
452
+ this.listView.update(entries, this.textFilterRegExp);
453
+ }
454
+
455
+ private toCoverageListItem(info: URLCoverageInfo): CoverageListItem {
456
+ return {
457
+ url: info.url(),
458
+ type: info.type(),
459
+ size: info.size(),
460
+ usedSize: info.usedSize(),
461
+ unusedSize: info.unusedSize(),
462
+ usedPercentage: info.usedPercentage(),
463
+ unusedPercentage: info.unusedPercentage(),
464
+ sources: [...info.sourcesURLCoverageInfo.values()].map(this.toCoverageListItem, this),
465
+ isContentScript: info.isContentScript(),
466
+ generatedUrl: info instanceof SourceURLCoverageInfo ? info.generatedURLCoverageInfo.url() : undefined,
467
+ };
439
468
  }
440
469
 
441
470
  async stopRecording(): Promise<void> {
@@ -514,7 +543,7 @@ export class CoverageView extends UI.Widget.VBox {
514
543
 
515
544
  private updateViews(updatedEntries: CoverageInfo[]): void {
516
545
  this.updateStats();
517
- this.listView.update(this.model?.entries() || []);
546
+ this.updateListView();
518
547
  this.exportAction.setEnabled(this.model !== null && this.model.entries().length > 0);
519
548
  this.decorationManager?.update(updatedEntries);
520
549
  }
@@ -527,14 +556,15 @@ export class CoverageView extends UI.Widget.VBox {
527
556
  for (const info of this.model.entries()) {
528
557
  all.total += info.size();
529
558
  all.unused += info.unusedSize();
530
- if (this.isVisible(false, info)) {
559
+ const listItem = this.toCoverageListItem(info);
560
+ if (this.isVisible(listItem)) {
531
561
  if (this.textFilterRegExp?.test(info.url())) {
532
562
  filtered.total += info.size();
533
563
  filtered.unused += info.unusedSize();
534
564
  } else {
535
565
  // If it doesn't match the filter, calculate the stats from visible children if there are any
536
566
  for (const childInfo of info.sourcesURLCoverageInfo.values()) {
537
- if (this.isVisible(false, childInfo)) {
567
+ if (this.isVisible(this.toCoverageListItem(childInfo))) {
538
568
  filtered.total += childInfo.size();
539
569
  filtered.unused += childInfo.unusedSize();
540
570
  }
@@ -565,7 +595,7 @@ export class CoverageView extends UI.Widget.VBox {
565
595
  }
566
596
  const text = this.filterInput.value();
567
597
  this.textFilterRegExp = text ? Platform.StringUtilities.createPlainTextSearchRegex(text, 'i') : null;
568
- this.listView.updateFilterAndHighlight(this.textFilterRegExp);
598
+ this.updateListView();
569
599
  this.updateStats();
570
600
  }
571
601
 
@@ -579,31 +609,31 @@ export class CoverageView extends UI.Widget.VBox {
579
609
  const option = this.filterByTypeComboBox.selectedOption();
580
610
  const type = option?.value;
581
611
  this.typeFilterValue = parseInt(type || '', 10) || null;
582
- this.listView.updateFilterAndHighlight(this.textFilterRegExp);
612
+ this.updateListView();
583
613
  this.updateStats();
584
614
  }
585
615
 
586
- private isVisible(ignoreTextFilter: boolean, coverageInfo: URLCoverageInfo): boolean {
587
- const url = coverageInfo.url();
616
+ private isVisible(coverageInfo: CoverageListItem): boolean {
617
+ const url = coverageInfo.url;
588
618
  if (url.startsWith(CoverageView.EXTENSION_BINDINGS_URL_PREFIX)) {
589
619
  return false;
590
620
  }
591
- if (coverageInfo.isContentScript() && !this.showContentScriptsSetting.get()) {
621
+ if (coverageInfo.isContentScript && !this.showContentScriptsSetting.get()) {
592
622
  return false;
593
623
  }
594
- if (this.typeFilterValue && !(coverageInfo.type() & this.typeFilterValue)) {
624
+ if (this.typeFilterValue && !(coverageInfo.type & this.typeFilterValue)) {
595
625
  return false;
596
626
  }
597
627
  // If it's a parent, check if any children are visible
598
- if (coverageInfo.sourcesURLCoverageInfo.size > 0) {
599
- for (const sourceURLCoverageInfo of coverageInfo.sourcesURLCoverageInfo.values()) {
600
- if (this.isVisible(ignoreTextFilter, sourceURLCoverageInfo)) {
628
+ if (coverageInfo.sources.length > 0) {
629
+ for (const sourceURLCoverageInfo of coverageInfo.sources) {
630
+ if (this.isVisible(sourceURLCoverageInfo)) {
601
631
  return true;
602
632
  }
603
633
  }
604
634
  }
605
635
 
606
- return ignoreTextFilter || !this.textFilterRegExp || this.textFilterRegExp.test(url);
636
+ return !this.textFilterRegExp || this.textFilterRegExp.test(url);
607
637
  }
608
638
 
609
639
  async exportReport(): Promise<void> {
@@ -618,7 +648,7 @@ export class CoverageView extends UI.Widget.VBox {
618
648
  }
619
649
 
620
650
  selectCoverageItemByUrl(url: string): void {
621
- this.listView.selectByUrl(url);
651
+ this.listView.selectByUrl(url as Platform.DevToolsPath.UrlString);
622
652
  }
623
653
 
624
654
  static readonly EXTENSION_BINDINGS_URL_PREFIX = 'extensions::';
@@ -6,6 +6,7 @@ import * as Common from '../../core/common/common.js';
6
6
  import * as i18n from '../../core/i18n/i18n.js';
7
7
  import * as Platform from '../../core/platform/platform.js';
8
8
  import * as SDK from '../../core/sdk/sdk.js';
9
+ import * as Badges from '../../models/badges/badges.js';
9
10
  import * as Lit from '../../ui/lit/lit.js';
10
11
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
11
12
 
@@ -202,6 +203,7 @@ export class NetworkThrottlingSelect extends Common.ObjectWrapper.ObjectWrapper<
202
203
 
203
204
  const onSelect = (conditions: SDK.NetworkManager.Conditions): void => {
204
205
  this.dispatchEventToListeners(Events.CONDITIONS_CHANGED, conditions);
206
+ Badges.UserBadges.instance().recordAction(Badges.BadgeAction.NETWORK_SPEED_THROTTLED);
205
207
  };
206
208
 
207
209
  const throttlingGroups = [