chrome-devtools-frontend 1.0.1519267 → 1.0.1520139

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 (90) 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 +12 -3
  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 +60 -34
  10. package/front_end/models/ai_assistance/agents/PerformanceAnnotationsAgent.ts +4 -4
  11. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +127 -29
  12. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +100 -55
  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 +23 -19
  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/javascript_metadata/NativeFunctions.js +5 -1
  22. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +14 -7
  23. package/front_end/panels/ai_assistance/PatchWidget.ts +17 -55
  24. package/front_end/panels/ai_assistance/components/ChatView.ts +44 -68
  25. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +47 -1
  26. package/front_end/panels/ai_assistance/components/chatView.css +12 -0
  27. package/front_end/panels/animation/AnimationTimeline.ts +1 -1
  28. package/front_end/panels/animation/animationTimeline.css +4 -0
  29. package/front_end/panels/application/preloading/components/PreloadingString.ts +2 -5
  30. package/front_end/panels/common/AiCodeCompletionTeaser.ts +5 -0
  31. package/front_end/panels/common/aiCodeCompletionTeaser.css +6 -1
  32. package/front_end/panels/console/ConsolePrompt.ts +6 -0
  33. package/front_end/panels/console/ConsoleView.ts +4 -2
  34. package/front_end/panels/coverage/CoverageListView.ts +133 -158
  35. package/front_end/panels/coverage/CoverageView.ts +39 -16
  36. package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +2 -0
  37. package/front_end/panels/network/NetworkDataGridNode.ts +22 -0
  38. package/front_end/panels/network/NetworkLogViewColumns.ts +9 -0
  39. package/front_end/panels/recorder/components/CreateRecordingView.ts +2 -0
  40. package/front_end/panels/search/SearchResultsPane.ts +48 -15
  41. package/front_end/panels/search/SearchView.ts +33 -30
  42. package/front_end/panels/search/searchView.css +0 -2
  43. package/front_end/panels/settings/components/SyncSection.ts +3 -3
  44. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +1 -4
  45. package/front_end/panels/sources/DebuggerPlugin.ts +4 -0
  46. package/front_end/panels/timeline/ThirdPartyTreeView.ts +1 -1
  47. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +0 -8
  48. package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -5
  49. package/front_end/panels/timeline/TimelinePanel.ts +2 -0
  50. package/front_end/panels/timeline/TimelineTreeView.ts +1 -1
  51. package/front_end/third_party/chromium/README.chromium +1 -1
  52. package/front_end/third_party/puppeteer/README.chromium +2 -2
  53. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
  54. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
  55. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
  56. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  57. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js +1 -1
  58. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js.map +1 -1
  59. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.d.ts.map +1 -1
  60. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js +15 -16
  61. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js.map +1 -1
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  63. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  64. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js +1 -1
  65. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js.map +1 -1
  66. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  67. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +4 -4
  68. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
  70. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js +1 -1
  71. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js.map +1 -1
  72. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.d.ts.map +1 -1
  73. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js +15 -16
  74. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js.map +1 -1
  75. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  76. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  77. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js +1 -1
  78. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js.map +1 -1
  79. package/front_end/third_party/puppeteer/package/package.json +3 -2
  80. package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
  81. package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +1 -1
  82. package/front_end/third_party/puppeteer/package/src/node/PipeTransport.ts +15 -17
  83. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  84. package/front_end/third_party/puppeteer/package/src/util/Function.ts +1 -1
  85. package/front_end/tsconfig.json +12 -1
  86. package/front_end/ui/legacy/InspectorView.ts +86 -13
  87. package/front_end/ui/legacy/TabbedPane.ts +2 -1
  88. package/front_end/ui/visual_logging/KnownContextValues.ts +6 -0
  89. package/front_end/ui/visual_logging/LoggingEvents.ts +1 -1
  90. 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,15 +140,15 @@ 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>;
144
+ private readonly isVisibleFilter: (arg0: CoverageListItem) => boolean;
133
145
  private highlightRegExp: RegExp|null;
134
146
  private dataGrid: DataGrid.SortableDataGrid.SortableDataGrid<GridNode>;
135
147
 
136
- constructor(isVisibleFilter: (arg0: URLCoverageInfo) => boolean) {
148
+ constructor(isVisibleFilter: (arg0: CoverageListItem) => boolean) {
137
149
  super({useShadowDom: true});
138
150
  this.registerRequiredCSS(coverageListViewStyles);
139
- this.nodeForCoverageInfo = new Map();
151
+ this.nodeForUrl = new Map();
140
152
  this.isVisibleFilter = isVisibleFilter;
141
153
  this.highlightRegExp = null;
142
154
 
@@ -179,90 +191,87 @@ export class CoverageListView extends UI.Widget.VBox {
179
191
  weight: 1,
180
192
  },
181
193
  ] 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
- });
194
+ this.dataGrid =
195
+ DataGrid.SortableDataGrid.SortableDataGrid.create(['dummy'], [], i18nString(UIStrings.codeCoverage)) as
196
+ DataGrid.SortableDataGrid.SortableDataGrid<GridNode>;
197
+ this.dataGrid.removeColumn('dummy');
198
+ for (const column of columns) {
199
+ this.dataGrid.addColumn(column);
200
+ }
201
+ this.dataGrid.setColumnsVisibility(new Set(columns.map(column => column.id)));
188
202
  this.dataGrid.setResizeMethod(DataGrid.DataGrid.ResizeMethod.LAST);
189
203
  this.dataGrid.setStriped(true);
190
204
  this.dataGrid.element.classList.add('flex-auto');
191
205
  this.dataGrid.addEventListener(DataGrid.DataGrid.Events.OPENED_NODE, this.onOpenedNode, this);
192
- this.dataGrid.addEventListener(DataGrid.DataGrid.Events.SORTING_CHANGED, this.sortingChanged, this);
193
206
 
194
207
  const dataGridWidget = this.dataGrid.asWidget();
195
208
  dataGridWidget.show(this.contentElement);
196
209
  this.setDefaultFocusedChild(dataGridWidget);
197
210
  }
198
211
 
199
- update(coverageInfo: URLCoverageInfo[] = []): void {
212
+ update(coverageInfo: CoverageListItem[] = []): void {
200
213
  let hadUpdates = false;
201
- const maxSize = coverageInfo.reduce((acc, entry) => Math.max(acc, entry.size()), 0);
214
+ const maxSize = coverageInfo.reduce((acc, entry) => Math.max(acc, entry.size), 0);
202
215
  const rootNode = this.dataGrid.rootNode();
203
216
  for (const entry of coverageInfo) {
204
- let node = this.nodeForCoverageInfo.get(entry);
217
+ let node = this.nodeForUrl.get(entry.url);
205
218
  if (node) {
206
219
  if (this.isVisibleFilter(node.coverageInfo)) {
207
- hadUpdates = node.refreshIfNeeded(maxSize) || hadUpdates;
208
- if (entry.sourcesURLCoverageInfo.size > 0) {
209
- this.updateSourceNodes(entry.sourcesURLCoverageInfo, maxSize, node);
220
+ hadUpdates = node.refreshIfNeeded(maxSize, entry) || hadUpdates;
221
+ if (entry.sources.length > 0) {
222
+ this.updateSourceNodes(entry.sources, maxSize, node);
210
223
  }
211
224
  }
212
225
  continue;
213
226
  }
214
227
  node = new GridNode(entry, maxSize);
215
- this.nodeForCoverageInfo.set(entry, node);
228
+ this.nodeForUrl.set(entry.url, node);
216
229
  if (this.isVisibleFilter(node.coverageInfo)) {
217
230
  rootNode.appendChild(node);
218
- if (entry.sourcesURLCoverageInfo.size > 0) {
219
- void this.createSourceNodes(entry.sourcesURLCoverageInfo, maxSize, node);
231
+ if (entry.sources.length > 0) {
232
+ void this.createSourceNodes(entry.sources, maxSize, node);
220
233
  }
221
234
  hadUpdates = true;
222
235
  }
223
236
  }
224
237
  if (hadUpdates) {
225
- this.sortingChanged();
238
+ this.dataGrid.dispatchEventToListeners(DataGrid.DataGrid.Events.SORTING_CHANGED);
226
239
  }
227
240
  }
228
241
 
229
- updateSourceNodes(
230
- sourcesURLCoverageInfo: Map<Platform.DevToolsPath.UrlString, SourceURLCoverageInfo>, maxSize: number,
231
- node: GridNode): void {
242
+ updateSourceNodes(sources: CoverageListItem[], maxSize: number, node: GridNode): void {
232
243
  let shouldCreateSourceNodes = false;
233
- for (const coverageInfo of sourcesURLCoverageInfo.values()) {
234
- const sourceNode = this.nodeForCoverageInfo.get(coverageInfo);
244
+ for (const coverageInfo of sources) {
245
+ const sourceNode = this.nodeForUrl.get(coverageInfo.url);
235
246
  if (sourceNode) {
236
- sourceNode.refreshIfNeeded(maxSize);
247
+ sourceNode.refreshIfNeeded(maxSize, coverageInfo);
237
248
  } else {
238
249
  shouldCreateSourceNodes = true;
239
250
  break;
240
251
  }
241
252
  }
242
253
  if (shouldCreateSourceNodes) {
243
- void this.createSourceNodes(sourcesURLCoverageInfo, maxSize, node);
254
+ void this.createSourceNodes(sources, maxSize, node);
244
255
  }
245
256
  }
246
257
 
247
- async createSourceNodes(
248
- sourcesURLCoverageInfo: Map<Platform.DevToolsPath.UrlString, SourceURLCoverageInfo>, maxSize: number,
249
- node: GridNode): Promise<void> {
250
- for (const coverageInfo of sourcesURLCoverageInfo.values()) {
258
+ async createSourceNodes(sources: CoverageListItem[], maxSize: number, node: GridNode): Promise<void> {
259
+ for (const coverageInfo of sources) {
251
260
  const sourceNode = new GridNode(coverageInfo, maxSize);
252
261
  node.appendChild(sourceNode);
253
- this.nodeForCoverageInfo.set(coverageInfo, sourceNode);
262
+ this.nodeForUrl.set(coverageInfo.url, sourceNode);
254
263
  }
255
264
  }
256
265
 
257
266
  reset(): void {
258
- this.nodeForCoverageInfo.clear();
267
+ this.nodeForUrl.clear();
259
268
  this.dataGrid.rootNode().removeChildren();
260
269
  }
261
270
 
262
271
  updateFilterAndHighlight(highlightRegExp: RegExp|null): void {
263
272
  this.highlightRegExp = highlightRegExp;
264
273
  let hadTreeUpdates = false;
265
- for (const node of this.nodeForCoverageInfo.values()) {
274
+ for (const node of this.nodeForUrl.values()) {
266
275
  const shouldBeVisible = this.isVisibleFilter(node.coverageInfo);
267
276
  const isVisible = Boolean(node.parent);
268
277
  if (shouldBeVisible) {
@@ -279,13 +288,13 @@ export class CoverageListView extends UI.Widget.VBox {
279
288
  }
280
289
  }
281
290
  if (hadTreeUpdates) {
282
- this.sortingChanged();
291
+ this.dataGrid.dispatchEventToListeners(DataGrid.DataGrid.Events.SORTING_CHANGED);
283
292
  }
284
293
  }
285
294
 
286
295
  private appendNodeByType(node: GridNode): void {
287
- if (node.coverageInfo instanceof SourceURLCoverageInfo) {
288
- const parentNode = this.nodeForCoverageInfo.get(node.coverageInfo.generatedURLCoverageInfo);
296
+ if (node.coverageInfo.generatedUrl) {
297
+ const parentNode = this.nodeForUrl.get(node.coverageInfo.generatedUrl);
289
298
  parentNode?.appendChild(node);
290
299
  } else {
291
300
  this.dataGrid.rootNode().appendChild(node);
@@ -293,11 +302,9 @@ export class CoverageListView extends UI.Widget.VBox {
293
302
  }
294
303
 
295
304
  selectByUrl(url: string): void {
296
- for (const [info, node] of this.nodeForCoverageInfo.entries()) {
297
- if (info.url() === url) {
298
- node.revealAndSelect();
299
- break;
300
- }
305
+ const node = this.nodeForUrl.get(url as Platform.DevToolsPath.UrlString);
306
+ if (node) {
307
+ node.revealAndSelect();
301
308
  }
302
309
  }
303
310
 
@@ -311,7 +318,7 @@ export class CoverageListView extends UI.Widget.VBox {
311
318
  return;
312
319
  }
313
320
  const coverageInfo = (node as GridNode).coverageInfo;
314
- const sourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(coverageInfo.url());
321
+ const sourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(coverageInfo.url);
315
322
  if (!sourceCode) {
316
323
  return;
317
324
  }
@@ -321,21 +328,6 @@ export class CoverageListView extends UI.Widget.VBox {
321
328
  }
322
329
  void Common.Revealer.reveal(sourceCode);
323
330
  }
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
331
  }
340
332
 
341
333
  let percentageFormatter: Intl.NumberFormat|null = null;
@@ -360,18 +352,28 @@ function getBytesFormatter(): Intl.NumberFormat {
360
352
  }
361
353
 
362
354
  export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<GridNode> {
363
- coverageInfo: URLCoverageInfo;
355
+ coverageInfo: CoverageListItem;
364
356
  private lastUsedSize!: number|undefined;
365
357
  private url: Platform.DevToolsPath.UrlString;
366
358
  private maxSize: number;
367
359
  private highlightRegExp: RegExp|null;
368
360
 
369
- constructor(coverageInfo: URLCoverageInfo, maxSize: number) {
361
+ constructor(coverageInfo: CoverageListItem, maxSize: number) {
370
362
  super();
371
363
  this.coverageInfo = coverageInfo;
372
- this.url = coverageInfo.url();
364
+ this.url = coverageInfo.url;
373
365
  this.maxSize = maxSize;
374
366
  this.highlightRegExp = null;
367
+ this.#updateData(coverageInfo);
368
+ }
369
+
370
+ #updateData(coverageInfo: CoverageListItem): void {
371
+ this.data['url'] = this.url;
372
+ this.data['type'] = coverageTypeToString(coverageInfo.type);
373
+ this.data['size'] = coverageInfo.size;
374
+ this.data['unused-size'] = coverageInfo.unusedSize;
375
+ this.data['bars'] = coverageInfo.unusedSize;
376
+ this.coverageInfo = coverageInfo;
375
377
  }
376
378
 
377
379
  setHighlight(highlightRegExp: RegExp|null): void {
@@ -382,102 +384,98 @@ export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<Gri
382
384
  this.refresh();
383
385
  }
384
386
 
385
- refreshIfNeeded(maxSize: number): boolean {
386
- if (this.lastUsedSize === this.coverageInfo.usedSize() && maxSize === this.maxSize) {
387
+ refreshIfNeeded(maxSize: number, coverageInfo: CoverageListItem): boolean {
388
+ if (this.lastUsedSize === coverageInfo.usedSize && maxSize === this.maxSize) {
387
389
  return false;
388
390
  }
389
- this.lastUsedSize = this.coverageInfo.usedSize();
391
+ this.lastUsedSize = coverageInfo.usedSize;
390
392
  this.maxSize = maxSize;
391
393
  this.refresh();
394
+ this.#updateData(coverageInfo);
392
395
  return true;
393
396
  }
394
397
 
395
398
  override createCell(columnId: string): HTMLElement {
396
399
  const cell = this.createTD(columnId);
400
+ const info = this.coverageInfo;
401
+ const formatBytes = (value: number|undefined): string => {
402
+ return getBytesFormatter().format(value ?? 0);
403
+ };
404
+ const formatPercent = (value: number|undefined): string => {
405
+ return getPercentageFormatter().format(value ?? 0);
406
+ };
397
407
  switch (columnId) {
398
408
  case 'url': {
399
409
  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');
410
+ this.setCellAccessibleName(this.url, cell, columnId);
403
411
  const splitURL = /^(.*)(\/[^/]*)$/.exec(this.url);
404
- prefix.textContent = splitURL ? splitURL[1] : this.url;
405
- suffix.textContent = splitURL ? splitURL[2] : '';
412
+ render(
413
+ html`
414
+ <div class="url-outer">
415
+ <div class="url-prefix">${splitURL ? splitURL[1] : this.url}</div>
416
+ <div class="url-suffix">${splitURL ? splitURL[2] : ''}</div>
417
+ </div>`,
418
+ cell);
406
419
  if (this.highlightRegExp) {
407
- this.highlight(outer, this.url);
420
+ this.highlight(cell, this.url);
408
421
  }
409
- this.setCellAccessibleName(this.url, cell, columnId);
410
422
  break;
411
423
  }
412
424
  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
- }
425
+ UI.Tooltip.Tooltip.install(
426
+ cell,
427
+ info.type & CoverageType.JAVA_SCRIPT_PER_FUNCTION ? i18nString(UIStrings.jsCoverageWithPerFunction) :
428
+ info.type & CoverageType.JAVA_SCRIPT ? i18nString(UIStrings.jsCoverageWithPerBlock) :
429
+ '');
430
+ render(coverageTypeToString(this.coverageInfo.type), cell);
419
431
  break;
420
432
  }
421
433
  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);
434
+ this.setCellAccessibleName(i18nString(UIStrings.sBytes, {n: info.size || 0}), cell, columnId);
435
+ render(html`<span>${formatBytes(info.size)}</span>`, cell);
428
436
  break;
429
437
  }
430
438
  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);
439
+ this.setCellAccessibleName(
440
+ i18nString(UIStrings.sBytesS, {n: info.unusedSize, percentage: formatPercent(info.unusedPercentage)}), cell,
441
+ columnId);
442
+ // clang-format off
443
+ render(html`
444
+ <span>${formatBytes(info.unusedSize)}</span>
445
+ <span class="percent-value">
446
+ ${formatPercent(info.unusedPercentage)}
447
+ </span>`, cell);
448
+ // clang-format on
440
449
  break;
441
450
  }
442
451
  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
452
  this.setCellAccessibleName(
480
- i18nString(UIStrings.sOfFileUnusedSOfFileUsed, {PH1: unusedPercent, PH2: usedPercent}), cell, columnId);
453
+ i18nString(
454
+ UIStrings.sOfFileUnusedSOfFileUsed,
455
+ {PH1: formatPercent(info.unusedPercentage), PH2: formatPercent(info.usedPercentage)}),
456
+ cell, columnId);
457
+ // clang-format off
458
+ render(html`
459
+ <div class="bar-container">
460
+ ${info.unusedSize > 0 ? html`
461
+ <div class="bar bar-unused-size"
462
+ title=${
463
+ info.type & CoverageType.JAVA_SCRIPT_PER_FUNCTION ? i18nString(UIStrings.sBytesSBelongToFunctionsThatHave, {PH1: info.unusedSize, PH2: formatPercent(info.unusedPercentage)}) :
464
+ info.type & CoverageType.JAVA_SCRIPT ? i18nString(UIStrings.sBytesSBelongToBlocksOf, {PH1: info.unusedSize, PH2: formatPercent(info.unusedPercentage)}) :
465
+ ''}
466
+ style=${styleMap({width: ((info.unusedSize / this.maxSize) * 100 || 0) + '%'})}>
467
+ </div>` : nothing}
468
+ ${info.usedSize > 0 ? html`
469
+ <div class="bar bar-used-size"
470
+ title=${
471
+ info.type & CoverageType.JAVA_SCRIPT_PER_FUNCTION ? i18nString(UIStrings.sBytesSBelongToFunctionsThatHaveExecuted, {PH1: info.usedSize, PH2: formatPercent(info.usedPercentage)}) :
472
+ info.type & CoverageType.JAVA_SCRIPT ? i18nString(UIStrings.sBytesSBelongToBlocksOfJavascript, {PH1: info.usedSize, PH2: formatPercent(info.usedPercentage)}) :
473
+ ''}
474
+ { PH1: info.usedSize, PH2: formatPercent(info.usedPercentage) })}
475
+ style=${styleMap({width:((info.usedSize / this.maxSize) * 100 || 0) + '%'})}>
476
+ </div>` : nothing}
477
+ </div>`, cell);
478
+ // clang-format on
481
479
  }
482
480
  }
483
481
  return cell;
@@ -494,27 +492,4 @@ export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<Gri
494
492
  const range = new TextUtils.TextRange.SourceRange(matches.index, matches[0].length);
495
493
  UI.UIUtils.highlightRangesWithStyleClass(element, [range], 'filter-highlight');
496
494
  }
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
495
  }
@@ -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(this.isVisible.bind(this));
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,22 @@ export class CoverageView extends UI.Widget.VBox {
435
442
  }
436
443
 
437
444
  private updateListView(): void {
438
- this.listView.update(this.model?.entries() || []);
445
+ this.listView.update(this.model?.entries().map(this.toCoverageListItem, this) || []);
446
+ }
447
+
448
+ private toCoverageListItem(info: URLCoverageInfo): CoverageListItem {
449
+ return {
450
+ url: info.url(),
451
+ type: info.type(),
452
+ size: info.size(),
453
+ usedSize: info.usedSize(),
454
+ unusedSize: info.unusedSize(),
455
+ usedPercentage: info.usedPercentage(),
456
+ unusedPercentage: info.unusedPercentage(),
457
+ sources: [...info.sourcesURLCoverageInfo.values()].map(this.toCoverageListItem, this),
458
+ isContentScript: info.isContentScript(),
459
+ generatedUrl: info instanceof SourceURLCoverageInfo ? info.generatedURLCoverageInfo.url() : undefined,
460
+ };
439
461
  }
440
462
 
441
463
  async stopRecording(): Promise<void> {
@@ -514,7 +536,7 @@ export class CoverageView extends UI.Widget.VBox {
514
536
 
515
537
  private updateViews(updatedEntries: CoverageInfo[]): void {
516
538
  this.updateStats();
517
- this.listView.update(this.model?.entries() || []);
539
+ this.listView.update(this.model?.entries().map(this.toCoverageListItem, this) || []);
518
540
  this.exportAction.setEnabled(this.model !== null && this.model.entries().length > 0);
519
541
  this.decorationManager?.update(updatedEntries);
520
542
  }
@@ -527,14 +549,15 @@ export class CoverageView extends UI.Widget.VBox {
527
549
  for (const info of this.model.entries()) {
528
550
  all.total += info.size();
529
551
  all.unused += info.unusedSize();
530
- if (this.isVisible(false, info)) {
552
+ const listItem = this.toCoverageListItem(info);
553
+ if (this.isVisible(listItem)) {
531
554
  if (this.textFilterRegExp?.test(info.url())) {
532
555
  filtered.total += info.size();
533
556
  filtered.unused += info.unusedSize();
534
557
  } else {
535
558
  // If it doesn't match the filter, calculate the stats from visible children if there are any
536
559
  for (const childInfo of info.sourcesURLCoverageInfo.values()) {
537
- if (this.isVisible(false, childInfo)) {
560
+ if (this.isVisible(this.toCoverageListItem(childInfo))) {
538
561
  filtered.total += childInfo.size();
539
562
  filtered.unused += childInfo.unusedSize();
540
563
  }
@@ -583,27 +606,27 @@ export class CoverageView extends UI.Widget.VBox {
583
606
  this.updateStats();
584
607
  }
585
608
 
586
- private isVisible(ignoreTextFilter: boolean, coverageInfo: URLCoverageInfo): boolean {
587
- const url = coverageInfo.url();
609
+ private isVisible(coverageInfo: CoverageListItem): boolean {
610
+ const url = coverageInfo.url;
588
611
  if (url.startsWith(CoverageView.EXTENSION_BINDINGS_URL_PREFIX)) {
589
612
  return false;
590
613
  }
591
- if (coverageInfo.isContentScript() && !this.showContentScriptsSetting.get()) {
614
+ if (coverageInfo.isContentScript && !this.showContentScriptsSetting.get()) {
592
615
  return false;
593
616
  }
594
- if (this.typeFilterValue && !(coverageInfo.type() & this.typeFilterValue)) {
617
+ if (this.typeFilterValue && !(coverageInfo.type & this.typeFilterValue)) {
595
618
  return false;
596
619
  }
597
620
  // 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)) {
621
+ if (coverageInfo.sources.length > 0) {
622
+ for (const sourceURLCoverageInfo of coverageInfo.sources) {
623
+ if (this.isVisible(sourceURLCoverageInfo)) {
601
624
  return true;
602
625
  }
603
626
  }
604
627
  }
605
628
 
606
- return ignoreTextFilter || !this.textFilterRegExp || this.textFilterRegExp.test(url);
629
+ return !this.textFilterRegExp || this.textFilterRegExp.test(url);
607
630
  }
608
631
 
609
632
  async exportReport(): Promise<void> {
@@ -618,7 +641,7 @@ export class CoverageView extends UI.Widget.VBox {
618
641
  }
619
642
 
620
643
  selectCoverageItemByUrl(url: string): void {
621
- this.listView.selectByUrl(url);
644
+ this.listView.selectByUrl(url as Platform.DevToolsPath.UrlString);
622
645
  }
623
646
 
624
647
  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 = [
@@ -771,6 +771,24 @@ export class NetworkRequestNode extends NetworkNode {
771
771
  return aScore - bScore || aRequest.identityCompare(bRequest);
772
772
  }
773
773
 
774
+ static IsAdRelatedComparator(a: NetworkNode, b: NetworkNode): number {
775
+ // TODO(allada) Handle this properly for group nodes.
776
+ const aRequest = a.requestOrFirstKnownChildRequest();
777
+ const bRequest = b.requestOrFirstKnownChildRequest();
778
+ if (!aRequest || !bRequest) {
779
+ return !aRequest ? -1 : 1;
780
+ }
781
+ const aIsAdRelated = aRequest.isAdRelated();
782
+ const bIsAdRelated = bRequest.isAdRelated();
783
+ if (aIsAdRelated > bIsAdRelated) {
784
+ return 1;
785
+ }
786
+ if (bIsAdRelated > aIsAdRelated) {
787
+ return -1;
788
+ }
789
+ return aRequest.identityCompare(bRequest);
790
+ }
791
+
774
792
  static RequestPropertyComparator(propertyName: string, a: NetworkNode, b: NetworkNode): number {
775
793
  // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
776
794
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -1038,6 +1056,10 @@ export class NetworkRequestNode extends NetworkNode {
1038
1056
  this.renderAddressSpaceCell(cell, this.requestInternal.remoteAddressSpace());
1039
1057
  break;
1040
1058
  }
1059
+ case 'is-ad-related': {
1060
+ this.setTextAndTitle(cell, this.requestInternal.isAdRelated().toLocaleString());
1061
+ break;
1062
+ }
1041
1063
  case 'cookies': {
1042
1064
  this.setTextAndTitle(cell, this.arrayLength(this.requestInternal.includedRequestCookies()));
1043
1065
  break;
@@ -148,6 +148,10 @@ const UIStrings = {
148
148
  * @description Text in Network Log View Columns of the Network panel
149
149
  */
150
150
  remoteAddressSpace: 'Remote Address Space',
151
+ /**
152
+ * @description Text to show whether a request is ad-related
153
+ */
154
+ isAdRelated: 'Is Ad-Related',
151
155
  } as const;
152
156
  const str_ = i18n.i18n.registerUIStrings('panels/network/NetworkLogViewColumns.ts', UIStrings);
153
157
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -1183,6 +1187,11 @@ const DEFAULT_COLUMNS = [
1183
1187
  title: i18n.i18n.lockedLazyString('User-Agent'),
1184
1188
  sortingFunction: NetworkRequestNode.RequestHeaderStringComparator.bind(null, 'user-agent'),
1185
1189
  },
1190
+ {
1191
+ id: 'is-ad-related',
1192
+ title: i18nLazyString(UIStrings.isAdRelated),
1193
+ sortingFunction: NetworkRequestNode.IsAdRelatedComparator,
1194
+ },
1186
1195
  // This header is a placeholder to let datagrid know that it can be sorted by this column, but never shown.
1187
1196
  {
1188
1197
  id: 'waterfall',