chrome-devtools-frontend 1.0.1525561 → 1.0.1526630

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 (58) hide show
  1. package/front_end/core/common/Settings.ts +1 -1
  2. package/front_end/core/i18n/i18nImpl.ts +1 -1
  3. package/front_end/core/sdk/ChildTargetManager.ts +2 -0
  4. package/front_end/core/sdk/PreloadingModel.ts +3 -0
  5. package/front_end/core/sdk/ResourceTreeModel.ts +1 -1
  6. package/front_end/core/sdk/SourceMapScopesInfo.ts +57 -0
  7. package/front_end/generated/InspectorBackendCommands.js +5 -5
  8. package/front_end/generated/SupportedCSSProperties.js +0 -19
  9. package/front_end/generated/protocol-mapping.d.ts +4 -2
  10. package/front_end/generated/protocol-proxy-api.d.ts +4 -2
  11. package/front_end/generated/protocol.ts +9 -8
  12. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +43 -8
  13. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +50 -32
  14. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +29 -29
  15. package/front_end/models/javascript_metadata/NativeFunctions.js +3 -8
  16. package/front_end/models/trace/handlers/UserTimingsHandler.ts +1 -1
  17. package/front_end/models/trace/insights/CLSCulprits.ts +2 -1
  18. package/front_end/models/trace/insights/Cache.ts +2 -1
  19. package/front_end/models/trace/insights/DOMSize.ts +2 -1
  20. package/front_end/models/trace/insights/DocumentLatency.ts +2 -1
  21. package/front_end/models/trace/insights/DuplicatedJavaScript.ts +2 -1
  22. package/front_end/models/trace/insights/FontDisplay.ts +2 -1
  23. package/front_end/models/trace/insights/ForcedReflow.ts +2 -1
  24. package/front_end/models/trace/insights/INPBreakdown.ts +2 -1
  25. package/front_end/models/trace/insights/ImageDelivery.ts +2 -1
  26. package/front_end/models/trace/insights/LCPBreakdown.ts +2 -1
  27. package/front_end/models/trace/insights/LCPDiscovery.ts +2 -1
  28. package/front_end/models/trace/insights/LegacyJavaScript.ts +2 -1
  29. package/front_end/models/trace/insights/ModernHTTP.ts +2 -1
  30. package/front_end/models/trace/insights/NetworkDependencyTree.ts +2 -1
  31. package/front_end/models/trace/insights/RenderBlocking.ts +2 -1
  32. package/front_end/models/trace/insights/SlowCSSSelector.ts +2 -1
  33. package/front_end/models/trace/insights/ThirdParties.ts +2 -1
  34. package/front_end/models/trace/insights/Viewport.ts +2 -1
  35. package/front_end/models/trace/insights/types.ts +2 -1
  36. package/front_end/panels/application/ReportingApiView.ts +8 -7
  37. package/front_end/panels/application/StorageView.ts +2 -1
  38. package/front_end/panels/application/preloading/components/PreloadingString.ts +2 -0
  39. package/front_end/panels/console/ConsolePrompt.ts +24 -4
  40. package/front_end/panels/coverage/CoverageListView.ts +125 -279
  41. package/front_end/panels/coverage/CoverageView.ts +109 -111
  42. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorPane.ts +11 -19
  43. package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +27 -43
  44. package/front_end/panels/network/RequestResponseView.ts +1 -1
  45. package/front_end/panels/timeline/TimelinePanel.ts +10 -8
  46. package/front_end/panels/timeline/components/ExportTraceOptions.ts +1 -1
  47. package/front_end/third_party/chromium/README.chromium +1 -1
  48. package/front_end/ui/components/buttons/Button.ts +18 -1
  49. package/front_end/ui/legacy/EmptyWidget.ts +11 -1
  50. package/front_end/ui/legacy/SearchableView.ts +1 -1
  51. package/front_end/ui/legacy/Toolbar.ts +25 -4
  52. package/front_end/ui/legacy/UIUtils.ts +28 -2
  53. package/front_end/ui/legacy/Widget.ts +5 -0
  54. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +48 -5
  55. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +2 -2
  56. package/front_end/ui/legacy/components/source_frame/JSONView.ts +28 -0
  57. package/front_end/ui/legacy/components/source_frame/StreamingContentHexView.ts +7 -8
  58. package/package.json +22 -22
@@ -76,6 +76,7 @@ export type InsightModel<UIStrings extends Record<string, string> = Record<strin
76
76
  strings: UIStrings,
77
77
  title: Common.UIString.LocalizedString,
78
78
  description: Common.UIString.LocalizedString,
79
+ docs: string,
79
80
  category: InsightCategory,
80
81
  state: 'pass' | 'fail' | 'informative',
81
82
  /** Used by RelatedInsightChips.ts */
@@ -101,7 +102,7 @@ export type InsightModel<UIStrings extends Record<string, string> = Record<strin
101
102
  };
102
103
 
103
104
  export type PartialInsightModel<T> =
104
- Omit<T, 'strings'|'title'|'description'|'category'|'state'|'insightKey'|'navigationId'|'frameId'>;
105
+ Omit<T, 'strings'|'title'|'description'|'docs'|'category'|'state'|'insightKey'|'navigationId'|'frameId'>;
105
106
 
106
107
  /**
107
108
  * Contains insights for a specific navigation. If a trace began after a navigation already started,
@@ -56,12 +56,11 @@ interface ViewInput {
56
56
  onReportSelected: (id: string) => void;
57
57
  }
58
58
 
59
- type View = (input: ViewInput, output: object, target: HTMLElement) => void;
60
-
61
- export const DEFAULT_VIEW: View = (input, _output, target) => {
59
+ export const DEFAULT_VIEW = (input: ViewInput, output: undefined, target: HTMLElement): void => {
62
60
  if (input.hasReports || input.hasEndpoints) {
63
61
  // clang-format off
64
62
  render(html`
63
+ <style>${UI.inspectorCommonStyles}</style>
65
64
  <devtools-split-view sidebar-position="second" sidebar-initial-size="150" jslog=${
66
65
  VisualLogging.pane('reporting-api')}>
67
66
  ${input.hasReports ? html`
@@ -73,9 +72,9 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
73
72
  </div>
74
73
  <div slot="sidebar" class="vbox" jslog=${VisualLogging.pane('preview').track({resize: true})}>
75
74
  ${input.focusedReport ? html`
76
- <devtools-widget .widgetConfig=${widgetConfig(
77
- element => SourceFrame.JSONView.JSONView.createViewSync(input.focusedReport?.body || '', element)
78
- )}></devtools-widget>
75
+ <devtools-widget .widgetConfig=${widgetConfig(SourceFrame.JSONView.SearchableJsonView, {
76
+ jsonObject: input.focusedReport.body,
77
+ })}></devtools-widget>
79
78
  ` : html`
80
79
  <devtools-widget .widgetConfig=${widgetConfig(UI.EmptyWidget.EmptyWidget, {
81
80
  header: i18nString(UIStrings.noReportSelected),
@@ -112,6 +111,8 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
112
111
  }
113
112
  };
114
113
 
114
+ type View = typeof DEFAULT_VIEW;
115
+
115
116
  export class ReportingApiView extends UI.Widget.VBox implements
116
117
  SDK.TargetManager.SDKModelObserver<SDK.NetworkManager.NetworkManager> {
117
118
  #endpoints: Map<string, Protocol.Network.ReportingApiEndpoint[]>;
@@ -164,7 +165,7 @@ export class ReportingApiView extends UI.Widget.VBox implements
164
165
  focusedReport: this.#focusedReport,
165
166
  onReportSelected: this.#onReportSelected.bind(this),
166
167
  };
167
- this.#view(viewInput, {}, this.element);
168
+ this.#view(viewInput, undefined, this.element);
168
169
  }
169
170
 
170
171
  #onEndpointsChangedForOrigin({data}: {data: Protocol.Network.ReportingApiEndpointsChangedForOriginEvent}): void {
@@ -236,6 +236,7 @@ export class StorageView extends UI.ThrottledWidget.ThrottledWidget {
236
236
  this.quotaOverrideCheckbox.addEventListener('click', this.onClickCheckbox.bind(this), false);
237
237
  this.quotaOverrideControlRow = quota.appendRow();
238
238
  this.quotaOverrideEditor = this.quotaOverrideControlRow.createChild('input', 'quota-override-notification-editor');
239
+ this.quotaOverrideEditor.setAttribute('placeholder', i18nString(UIStrings.pleaseEnterANumber));
239
240
  this.quotaOverrideEditor.setAttribute(
240
241
  'jslog', `${VisualLogging.textField('quota-override').track({change: true})}`);
241
242
  this.quotaOverrideControlRow.appendChild(UI.UIUtils.createLabel(i18nString(UIStrings.mb)));
@@ -402,7 +403,7 @@ export class StorageView extends UI.ThrottledWidget.ThrottledWidget {
402
403
  this.quotaOverrideControlRow.classList.remove('hidden');
403
404
  this.quotaOverrideCheckbox.checked = true;
404
405
  this.quotaOverrideEditor.value = this.previousOverrideFieldValue;
405
- this.quotaOverrideEditor.focus();
406
+ window.setTimeout(() => this.quotaOverrideEditor.focus(), 500);
406
407
  } else if (this.target && this.securityOrigin) {
407
408
  this.quotaOverrideControlRow.classList.add('hidden');
408
409
  this.quotaOverrideCheckbox.checked = false;
@@ -736,6 +736,8 @@ export function capitalizedAction(action: Protocol.Preload.SpeculationAction): C
736
736
  return i18n.i18n.lockedString('Prefetch');
737
737
  case Protocol.Preload.SpeculationAction.Prerender:
738
738
  return i18n.i18n.lockedString('Prerender');
739
+ case Protocol.Preload.SpeculationAction.PrerenderUntilScript:
740
+ return i18n.i18n.lockedString('PrerenderUntilScript');
739
741
  }
740
742
  }
741
743
 
@@ -343,9 +343,29 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
343
343
  }
344
344
 
345
345
  private editorKeymap(): readonly CodeMirror.KeyBinding[] {
346
- const keymap = [
347
- {key: 'ArrowUp', run: () => this.#editorHistory.moveHistory(Direction.BACKWARD)},
348
- {key: 'ArrowDown', run: () => this.#editorHistory.moveHistory(Direction.FORWARD)},
346
+ const keymap: CodeMirror.KeyBinding[] = [
347
+ {
348
+ // Handle the KeyboardEvent manually.
349
+ any: (_view, event) => {
350
+ // Events with `repeat=true` are excluded from altering the history state because
351
+ // they are often not intended as such. Example:
352
+ // Scrolling through long snippets.
353
+ if (event.repeat) {
354
+ return false;
355
+ }
356
+
357
+ if (event.key === 'ArrowUp') {
358
+ return this.#editorHistory.moveHistory(Direction.BACKWARD);
359
+ }
360
+
361
+ if (event.key === 'ArrowDown') {
362
+ return this.#editorHistory.moveHistory(Direction.FORWARD);
363
+ }
364
+
365
+ return false;
366
+ },
367
+ },
368
+
349
369
  {mac: 'Ctrl-p', run: () => this.#editorHistory.moveHistory(Direction.BACKWARD, true)},
350
370
  {mac: 'Ctrl-n', run: () => this.#editorHistory.moveHistory(Direction.FORWARD, true)},
351
371
  {
@@ -372,7 +392,7 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
372
392
  if (this.isAiCodeCompletionEnabled()) {
373
393
  keymap.push({
374
394
  key: 'Tab',
375
- run: (): boolean => {
395
+ run: () => {
376
396
  const {accepted, suggestion} = TextEditor.Config.acceptAiAutoCompleteSuggestion(this.editor.editor);
377
397
  if (accepted) {
378
398
  this.dispatchEventToListeners(
@@ -1,17 +1,16 @@
1
1
  // Copyright 2017 The Chromium Authors
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
- /* eslint-disable rulesdir/no-imperative-dom-api */
5
- /* eslint-disable rulesdir/no-lit-render-outside-of-view */
4
+
5
+ import '../../ui/components/highlighting/highlighting.js';
6
+ import '../../ui/legacy/components/data_grid/data_grid.js';
6
7
 
7
8
  import * as Common from '../../core/common/common.js';
8
9
  import * as i18n from '../../core/i18n/i18n.js';
9
10
  import type * as Platform from '../../core/platform/platform.js';
10
- import * as TextUtils from '../../models/text_utils/text_utils.js';
11
11
  import * as Workspace from '../../models/workspace/workspace.js';
12
- import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
13
12
  import * as UI from '../../ui/legacy/legacy.js';
14
- import {Directives, html, nothing, render} from '../../ui/lit/lit.js';
13
+ import {Directives, html, nothing, render, type TemplateResult} from '../../ui/lit/lit.js';
15
14
 
16
15
  import coverageListViewStyles from './coverageListView.css.js';
17
16
  import {CoverageType} from './CoverageModel.js';
@@ -124,7 +123,7 @@ const UIStrings = {
124
123
  } as const;
125
124
  const str_ = i18n.i18n.registerUIStrings('panels/coverage/CoverageListView.ts', UIStrings);
126
125
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
127
- const {styleMap} = Directives;
126
+ const {styleMap, repeat} = Directives;
128
127
 
129
128
  export function coverageTypeToString(type: CoverageType): string {
130
129
  const types = [];
@@ -139,163 +138,95 @@ export function coverageTypeToString(type: CoverageType): string {
139
138
  return types.join('+');
140
139
  }
141
140
 
141
+ interface ViewInput {
142
+ items: CoverageListItem[];
143
+ selectedUrl: Platform.DevToolsPath.UrlString|null;
144
+ maxSize: number;
145
+ onOpen: (url: Platform.DevToolsPath.UrlString) => void;
146
+ highlightRegExp: RegExp|null;
147
+ }
148
+
149
+ type View = (input: ViewInput, output: object, target: HTMLElement) => void;
150
+
151
+ const formatBytes = (value: number|undefined): string => {
152
+ return getBytesFormatter().format(value ?? 0);
153
+ };
154
+ const formatPercent = (value: number|undefined): string => {
155
+ return getPercentageFormatter().format(value ?? 0);
156
+ };
157
+ export const DEFAULT_VIEW: View = (input, _output, target) => {
158
+ // clang-format off
159
+ render(html`
160
+ <style>${coverageListViewStyles}</style>
161
+ <devtools-data-grid class="flex-auto" name=${i18nString(UIStrings.codeCoverage)} striped autofocus resize="last"
162
+ .template=${html`
163
+ <table>
164
+ <tr>
165
+ <th id="url" width="250px" weight="3" sortable>${i18nString(UIStrings.url)}</th>
166
+ <th id="type" width="45px" weight="1" fixed sortable>${i18nString(UIStrings.type)}</th>
167
+ <th id="size" width="60px" align="right" weight="1" fixed sortable>${i18nString(UIStrings.totalBytes)}</th>
168
+ <th id="unused-size" width="100px" align="right" weight="1" fixed sortable sort="descending">${
169
+ i18nString(UIStrings.unusedBytes)}</th>
170
+ <th id="bars" width="250px" weight="1" sortable>${i18nString(UIStrings.usageVisualization)}</th>
171
+ </tr>
172
+ ${repeat(input.items, info => info.url, info => renderItem(info, input))}
173
+ </table>`}>
174
+ </devtools-data-grid>`,
175
+ target);
176
+ // clang-format on
177
+ };
178
+
142
179
  export class CoverageListView extends UI.Widget.VBox {
143
- private readonly nodeForUrl: Map<Platform.DevToolsPath.UrlString, GridNode>;
144
180
  private highlightRegExp: RegExp|null;
145
- private dataGrid: DataGrid.SortableDataGrid.SortableDataGrid<GridNode>;
181
+ #coverageInfo: CoverageListItem[] = [];
182
+ #selectedUrl: Platform.DevToolsPath.UrlString|null = null;
183
+ #maxSize = 0;
184
+ #view: View;
146
185
 
147
- constructor() {
148
- super({useShadowDom: true});
149
- this.registerRequiredCSS(coverageListViewStyles);
150
- this.nodeForUrl = new Map();
186
+ constructor(view = DEFAULT_VIEW) {
187
+ super({useShadowDom: true, delegatesFocus: true});
188
+ this.#view = view;
151
189
  this.highlightRegExp = null;
152
-
153
- const columns = [
154
- {
155
- id: 'url',
156
- title: i18nString(UIStrings.url),
157
- width: '250px',
158
- weight: 3,
159
- fixedWidth: false,
160
- sortable: true,
161
- disclosure: true,
162
- },
163
- {id: 'type', title: i18nString(UIStrings.type), width: '45px', weight: 1, fixedWidth: true, sortable: true},
164
- {
165
- id: 'size',
166
- title: i18nString(UIStrings.totalBytes),
167
- width: '60px',
168
- fixedWidth: true,
169
- sortable: true,
170
- align: DataGrid.DataGrid.Align.RIGHT,
171
- weight: 1,
172
- },
173
- {
174
- id: 'unused-size',
175
- title: i18nString(UIStrings.unusedBytes),
176
- width: '100px',
177
- fixedWidth: true,
178
- sortable: true,
179
- align: DataGrid.DataGrid.Align.RIGHT,
180
- sort: DataGrid.DataGrid.Order.Descending,
181
- weight: 1,
182
- },
183
- {
184
- id: 'bars',
185
- title: i18nString(UIStrings.usageVisualization),
186
- width: '250px',
187
- fixedWidth: false,
188
- sortable: true,
189
- weight: 1,
190
- },
191
- ] as DataGrid.DataGrid.ColumnDescriptor[];
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)));
200
- this.dataGrid.setResizeMethod(DataGrid.DataGrid.ResizeMethod.LAST);
201
- this.dataGrid.setStriped(true);
202
- this.dataGrid.element.classList.add('flex-auto');
203
- this.dataGrid.addEventListener(DataGrid.DataGrid.Events.OPENED_NODE, this.onOpenedNode, this);
204
-
205
- const dataGridWidget = this.dataGrid.asWidget();
206
- dataGridWidget.show(this.contentElement);
207
- this.setDefaultFocusedChild(dataGridWidget);
208
190
  }
209
191
 
210
192
  update(coverageInfo: CoverageListItem[], highlightRegExp: RegExp|null): void {
211
193
  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
-
222
- let hadUpdates = false;
223
- for (const entry of coverageInfo) {
224
- let node = this.nodeForUrl.get(entry.url);
225
- if (node) {
226
- hadUpdates = node.refreshIfNeeded(maxSize, entry) || hadUpdates;
227
- if (entry.sources.length > 0) {
228
- this.updateSourceNodes(entry.sources, maxSize, node);
229
- }
230
- node.setHighlight(this.highlightRegExp);
231
- continue;
232
- }
233
- node = new GridNode(entry, maxSize);
234
- this.nodeForUrl.set(entry.url, node);
235
- this.appendNodeByType(node);
236
- if (entry.sources.length > 0) {
237
- this.updateSourceNodes(entry.sources, maxSize, node);
238
- }
239
- node.setHighlight(this.highlightRegExp);
240
- hadUpdates = true;
241
- }
242
- if (hadUpdates) {
243
- this.dataGrid.dispatchEventToListeners(DataGrid.DataGrid.Events.SORTING_CHANGED);
244
- }
194
+ this.#maxSize = coverageInfo.reduce((acc, entry) => Math.max(acc, entry.size), 0);
195
+ this.#coverageInfo = coverageInfo;
196
+ this.requestUpdate();
245
197
  }
246
198
 
247
- updateSourceNodes(sources: CoverageListItem[], maxSize: number, node: GridNode): void {
248
- for (const coverageInfo of sources) {
249
- const sourceNode = this.nodeForUrl.get(coverageInfo.url);
250
- if (sourceNode) {
251
- sourceNode.refreshIfNeeded(maxSize, coverageInfo);
252
- } else {
253
- const sourceNode = new GridNode(coverageInfo, maxSize);
254
- node.appendChild(sourceNode);
255
- this.nodeForUrl.set(coverageInfo.url, sourceNode);
256
- }
257
- }
199
+ override performUpdate(): void {
200
+ const input: ViewInput = {
201
+ items: this.#coverageInfo,
202
+ selectedUrl: this.#selectedUrl,
203
+ maxSize: this.#maxSize,
204
+ onOpen: this.selectByUrl.bind(this),
205
+ highlightRegExp: this.highlightRegExp,
206
+ };
207
+ this.#view(input, {}, this.contentElement);
258
208
  }
259
209
 
260
210
  reset(): void {
261
- this.nodeForUrl.clear();
262
- this.dataGrid.rootNode().removeChildren();
211
+ this.#coverageInfo = [];
212
+ this.#maxSize = 0;
213
+ this.requestUpdate();
263
214
  }
264
215
 
265
- private appendNodeByType(node: GridNode): void {
266
- if (node.coverageInfo.generatedUrl) {
267
- const parentNode = this.nodeForUrl.get(node.coverageInfo.generatedUrl);
268
- parentNode?.appendChild(node);
269
- } else {
270
- this.dataGrid.rootNode().appendChild(node);
271
- }
272
- }
273
-
274
- selectByUrl(url: string): void {
275
- const node = this.nodeForUrl.get(url as Platform.DevToolsPath.UrlString);
276
- if (node) {
277
- node.revealAndSelect();
278
- }
279
- }
280
-
281
- private onOpenedNode(): void {
282
- void this.revealSourceForSelectedNode();
283
- }
284
-
285
- private async revealSourceForSelectedNode(): Promise<void> {
286
- const node = this.dataGrid.selectedNode;
287
- if (!node) {
216
+ selectByUrl(url: Platform.DevToolsPath.UrlString): void {
217
+ const info = this.#coverageInfo.find(info => info.url === url);
218
+ if (!info) {
288
219
  return;
289
220
  }
290
- const coverageInfo = (node as GridNode).coverageInfo;
291
- const sourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(coverageInfo.url);
221
+ if (this.#selectedUrl !== url) {
222
+ this.#selectedUrl = url as Platform.DevToolsPath.UrlString;
223
+ this.requestUpdate();
224
+ }
225
+ const sourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(url);
292
226
  if (!sourceCode) {
293
227
  return;
294
228
  }
295
229
 
296
- if (this.dataGrid.selectedNode !== node) {
297
- return;
298
- }
299
230
  void Common.Revealer.reveal(sourceCode);
300
231
  }
301
232
  }
@@ -321,148 +252,63 @@ function getBytesFormatter(): Intl.NumberFormat {
321
252
  return bytesFormatter;
322
253
  }
323
254
 
324
- export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<GridNode> {
325
- coverageInfo: CoverageListItem;
326
- private lastUsedSize!: number|undefined;
327
- private url: Platform.DevToolsPath.UrlString;
328
- private maxSize: number;
329
- private highlightRegExp: RegExp|null;
330
-
331
- constructor(coverageInfo: CoverageListItem, maxSize: number) {
332
- super();
333
- this.coverageInfo = coverageInfo;
334
- this.url = coverageInfo.url;
335
- this.maxSize = maxSize;
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;
347
- }
348
-
349
- setHighlight(highlightRegExp: RegExp|null): void {
350
- if (this.highlightRegExp === highlightRegExp) {
351
- return;
352
- }
353
- this.highlightRegExp = highlightRegExp;
354
- for (const child of this.children) {
355
- (child as GridNode).setHighlight(this.highlightRegExp);
356
- }
357
- this.refresh();
358
- }
359
-
360
- refreshIfNeeded(maxSize: number, coverageInfo: CoverageListItem): boolean {
361
- if (this.lastUsedSize === coverageInfo.usedSize && maxSize === this.maxSize) {
362
- return false;
363
- }
364
- this.lastUsedSize = coverageInfo.usedSize;
365
- this.maxSize = maxSize;
366
- this.refresh();
367
- this.#updateData(coverageInfo);
368
- return true;
255
+ function renderItem(info: CoverageListItem, input: ViewInput): TemplateResult {
256
+ function highlightRange(textContent: string): string {
257
+ const matches = input.highlightRegExp?.exec(textContent);
258
+ return matches?.length ? `${matches.index},${matches[0].length}` : '';
369
259
  }
370
260
 
371
- override createCell(columnId: string): HTMLElement {
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
- };
380
- switch (columnId) {
381
- case 'url': {
382
- UI.Tooltip.Tooltip.install(cell, this.url);
383
- this.setCellAccessibleName(this.url, cell, columnId);
384
- const splitURL = /^(.*)(\/[^/]*)$/.exec(this.url);
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);
392
- if (this.highlightRegExp) {
393
- this.highlight(cell, this.url);
394
- }
395
- break;
396
- }
397
- case 'type': {
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);
404
- break;
405
- }
406
- case 'size': {
407
- this.setCellAccessibleName(i18nString(UIStrings.sBytes, {n: info.size || 0}), cell, columnId);
408
- render(html`<span>${formatBytes(info.size)}</span>`, cell);
409
- break;
410
- }
411
- case 'unused-size': {
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
422
- break;
423
- }
424
- case 'bars': {
425
- this.setCellAccessibleName(
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`
261
+ const splitURL = /^(.*)(\/[^/]*)$/.exec(info.url);
262
+ // clang-format off
263
+ return html`
264
+ <style>${coverageListViewStyles}</style>
265
+ <tr data-url=${info.url} selected=${info.url === input.selectedUrl}
266
+ @open=${() => input.onOpen(info.url)}>
267
+ <td data-value=${info.url} title=${info.url} aria-label=${info.url}>
268
+ <devtools-highlight ranges=${highlightRange(info.url)} class="url-outer" aria-hidden="true">
269
+ <div class="url-prefix">${splitURL ? splitURL[1] : info.url}</div>
270
+ <div class="url-suffix">${splitURL ? splitURL[2] : ''}</div>
271
+ </devtools-highlight>
272
+ </td>
273
+ <td data-value=${coverageTypeToString(info.type)}
274
+ title=${info.type & CoverageType.JAVA_SCRIPT_PER_FUNCTION ? i18nString(UIStrings.jsCoverageWithPerFunction) :
275
+ info.type & CoverageType.JAVA_SCRIPT ? i18nString(UIStrings.jsCoverageWithPerBlock) :
276
+ ''}>
277
+ ${coverageTypeToString(info.type)}
278
+ </td>
279
+ <td data-value=${info.size} aria-label=${i18nString(UIStrings.sBytes, {n: info.size || 0})}>
280
+ <span>${formatBytes(info.size)}</span>
281
+ </td>
282
+ <td data-value=${info.unusedSize} aria-label=${i18nString(UIStrings.sBytesS, {n: info.unusedSize, percentage: formatPercent(info.unusedPercentage)})}>
283
+ <span>${formatBytes(info.unusedSize)}</span>
284
+ <span class="percent-value">
285
+ ${formatPercent(info.unusedPercentage)}
286
+ </span>
287
+ </td>
288
+ <td data-value=${info.unusedSize} aria-label=${i18nString(UIStrings.sOfFileUnusedSOfFileUsed, {PH1: formatPercent(info.unusedPercentage), PH2: formatPercent(info.usedPercentage)})}>
289
+ <div class="bar-container">
290
+ ${info.unusedSize > 0 ? html`
291
+ <div class="bar bar-unused-size"
292
+ title=${
293
+ info.type & CoverageType.JAVA_SCRIPT_PER_FUNCTION ? i18nString(UIStrings.sBytesSBelongToFunctionsThatHave, {PH1: info.unusedSize, PH2: formatPercent(info.unusedPercentage)}) :
294
+ info.type & CoverageType.JAVA_SCRIPT ? i18nString(UIStrings.sBytesSBelongToBlocksOf, {PH1: info.unusedSize, PH2: formatPercent(info.unusedPercentage)}) :
295
+ ''}
296
+ style=${styleMap({width: ((info.unusedSize / input.maxSize) * 100 || 0) + '%'})}>
297
+ </div>` : nothing}
298
+ ${info.usedSize > 0 ? html`
442
299
  <div class="bar bar-used-size"
443
300
  title=${
444
301
  info.type & CoverageType.JAVA_SCRIPT_PER_FUNCTION ? i18nString(UIStrings.sBytesSBelongToFunctionsThatHaveExecuted, {PH1: info.usedSize, PH2: formatPercent(info.usedPercentage)}) :
445
302
  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) + '%'})}>
303
+ ''}
304
+ style=${styleMap({width:((info.usedSize / input.maxSize) * 100 || 0) + '%'})}>
449
305
  </div>` : nothing}
450
- </div>`, cell);
451
- // clang-format on
452
- }
453
- }
454
- return cell;
455
- }
456
-
457
- private highlight(element: Element, textContent: string): void {
458
- if (!this.highlightRegExp) {
459
- return;
460
- }
461
- const matches = this.highlightRegExp.exec(textContent);
462
- if (!matches?.length) {
463
- return;
464
- }
465
- const range = new TextUtils.TextRange.SourceRange(matches.index, matches[0].length);
466
- UI.UIUtils.highlightRangesWithStyleClass(element, [range], 'filter-highlight');
467
- }
306
+ </div>
307
+ </td>
308
+ ${info.sources.length > 0 ? html`
309
+ <td><table>
310
+ ${repeat(info.sources, source => source.url, source => renderItem(source, input))}
311
+ </table></td>` : nothing}
312
+ </tr>`;
313
+ // clang-format on
468
314
  }