chrome-devtools-frontend 1.0.1526203 → 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.
@@ -68,7 +68,7 @@ export async function fetchAndRegisterLocaleData(
68
68
  locale: Intl.UnicodeBCP47LocaleIdentifier, location = self.location.toString()): Promise<void> {
69
69
  const localeDataTextPromise = fetch(getLocaleFetchUrl(locale, location)).then(result => result.json());
70
70
  const timeoutPromise =
71
- new Promise<never>((_, reject) => window.setTimeout(() => reject(new Error('timed out fetching locale')), 5000));
71
+ new Promise<never>((_, reject) => window.setTimeout(() => reject(new Error('timed out fetching locale')), 2500));
72
72
  const localeData = await Promise.race([timeoutPromise, localeDataTextPromise]);
73
73
  i18nInstance.registerLocaleData(locale, localeData);
74
74
  }
@@ -177,6 +177,8 @@ export class ChildTargetManager extends SDKModel<EventTypes> implements Protocol
177
177
  type = Type.FRAME;
178
178
  } else if (targetInfo.type === 'page') {
179
179
  type = Type.FRAME;
180
+ } else if (targetInfo.type === 'browser_ui') {
181
+ type = Type.FRAME;
180
182
  } else if (targetInfo.type === 'worker') {
181
183
  type = Type.Worker;
182
184
  } else if (targetInfo.type === 'worklet') {
@@ -858,7 +858,7 @@ inspectorBackend.registerCommand("Network.continueInterceptedRequest", [{"name":
858
858
  inspectorBackend.registerCommand("Network.deleteCookies", [{"name": "name", "type": "string", "optional": false, "description": "Name of the cookies to remove.", "typeRef": null}, {"name": "url", "type": "string", "optional": true, "description": "If specified, deletes all the cookies with the given name where domain and path match provided URL.", "typeRef": null}, {"name": "domain", "type": "string", "optional": true, "description": "If specified, deletes only cookies with the exact domain.", "typeRef": null}, {"name": "path", "type": "string", "optional": true, "description": "If specified, deletes only cookies with the exact path.", "typeRef": null}, {"name": "partitionKey", "type": "object", "optional": true, "description": "If specified, deletes only cookies with the the given name and partitionKey where all partition key attributes match the cookie partition key attribute.", "typeRef": "Network.CookiePartitionKey"}], [], "Deletes browser cookies with matching name and url or domain/path/partitionKey pair.");
859
859
  inspectorBackend.registerCommand("Network.disable", [], [], "Disables network tracking, prevents network events from being sent to the client.");
860
860
  inspectorBackend.registerCommand("Network.emulateNetworkConditions", [{"name": "offline", "type": "boolean", "optional": false, "description": "True to emulate internet disconnection.", "typeRef": null}, {"name": "latency", "type": "number", "optional": false, "description": "Minimum latency from request sent to response headers received (ms).", "typeRef": null}, {"name": "downloadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated download throughput (bytes/sec). -1 disables download throttling.", "typeRef": null}, {"name": "uploadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated upload throughput (bytes/sec). -1 disables upload throttling.", "typeRef": null}, {"name": "connectionType", "type": "string", "optional": true, "description": "Connection type if known.", "typeRef": "Network.ConnectionType"}, {"name": "packetLoss", "type": "number", "optional": true, "description": "WebRTC packet loss (percent, 0-100). 0 disables packet loss emulation, 100 drops all the packets.", "typeRef": null}, {"name": "packetQueueLength", "type": "number", "optional": true, "description": "WebRTC packet queue length (packet). 0 removes any queue length limitations.", "typeRef": null}, {"name": "packetReordering", "type": "boolean", "optional": true, "description": "WebRTC packetReordering feature.", "typeRef": null}], [], "Activates emulation of network conditions. This command is deprecated in favor of the emulateNetworkConditionsByRule and overrideNetworkState commands, which can be used together to the same effect.");
861
- inspectorBackend.registerCommand("Network.emulateNetworkConditionsByRule", [{"name": "offline", "type": "boolean", "optional": false, "description": "True to emulate internet disconnection.", "typeRef": null}, {"name": "matchedNetworkConditions", "type": "array", "optional": false, "description": "Configure conditions for matching requests. If multiple entries match a request, the first entry wins. Global conditions can be configured by leaving the urlPattern for the conditions empty. These global conditions are also applied for throttling of p2p connections.", "typeRef": "Network.NetworkConditions"}], ["ruleIds"], "Activates emulation of network conditions for individual requests using URL match patterns.");
861
+ inspectorBackend.registerCommand("Network.emulateNetworkConditionsByRule", [{"name": "offline", "type": "boolean", "optional": false, "description": "True to emulate internet disconnection.", "typeRef": null}, {"name": "matchedNetworkConditions", "type": "array", "optional": false, "description": "Configure conditions for matching requests. If multiple entries match a request, the first entry wins. Global conditions can be configured by leaving the urlPattern for the conditions empty. These global conditions are also applied for throttling of p2p connections.", "typeRef": "Network.NetworkConditions"}], ["ruleIds"], "Activates emulation of network conditions for individual requests using URL match patterns. Unlike the deprecated Network.emulateNetworkConditions this method does not affect `navigator` state. Use Network.overrideNetworkState to explicitly modify `navigator` behavior.");
862
862
  inspectorBackend.registerCommand("Network.overrideNetworkState", [{"name": "offline", "type": "boolean", "optional": false, "description": "True to emulate internet disconnection.", "typeRef": null}, {"name": "latency", "type": "number", "optional": false, "description": "Minimum latency from request sent to response headers received (ms).", "typeRef": null}, {"name": "downloadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated download throughput (bytes/sec). -1 disables download throttling.", "typeRef": null}, {"name": "uploadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated upload throughput (bytes/sec). -1 disables upload throttling.", "typeRef": null}, {"name": "connectionType", "type": "string", "optional": true, "description": "Connection type if known.", "typeRef": "Network.ConnectionType"}], [], "Override the state of navigator.onLine and navigator.connection.");
863
863
  inspectorBackend.registerCommand("Network.enable", [{"name": "maxTotalBufferSize", "type": "number", "optional": true, "description": "Buffer size in bytes to use when preserving network payloads (XHRs, etc).", "typeRef": null}, {"name": "maxResourceBufferSize", "type": "number", "optional": true, "description": "Per-resource buffer size in bytes to use when preserving network payloads (XHRs, etc).", "typeRef": null}, {"name": "maxPostDataSize", "type": "number", "optional": true, "description": "Longest post body size (in bytes) that would be included in requestWillBeSent notification", "typeRef": null}, {"name": "reportDirectSocketTraffic", "type": "boolean", "optional": true, "description": "Whether DirectSocket chunk send/receive events should be reported.", "typeRef": null}, {"name": "enableDurableMessages", "type": "boolean", "optional": true, "description": "Enable storing response bodies outside of renderer, so that these survive a cross-process navigation. Requires maxTotalBufferSize to be set. Currently defaults to false.", "typeRef": null}], [], "Enables network tracking, network events will now be delivered to the client.");
864
864
  inspectorBackend.registerCommand("Network.getAllCookies", [], ["cookies"], "Returns all browser cookies. Depending on the backend support, will return detailed cookie information in the `cookies` field. Deprecated. Use Storage.getCookies instead.");
@@ -3349,7 +3349,9 @@ export namespace ProtocolMapping {
3349
3349
  returnType: void;
3350
3350
  };
3351
3351
  /**
3352
- * Activates emulation of network conditions for individual requests using URL match patterns.
3352
+ * Activates emulation of network conditions for individual requests using URL match patterns. Unlike the deprecated
3353
+ * Network.emulateNetworkConditions this method does not affect `navigator` state. Use Network.overrideNetworkState to
3354
+ * explicitly modify `navigator` behavior.
3353
3355
  */
3354
3356
  'Network.emulateNetworkConditionsByRule': {
3355
3357
  paramsType: [Protocol.Network.EmulateNetworkConditionsByRuleRequest];
@@ -2523,7 +2523,9 @@ declare namespace ProtocolProxyApi {
2523
2523
  invoke_emulateNetworkConditions(params: Protocol.Network.EmulateNetworkConditionsRequest): Promise<Protocol.ProtocolResponseWithError>;
2524
2524
 
2525
2525
  /**
2526
- * Activates emulation of network conditions for individual requests using URL match patterns.
2526
+ * Activates emulation of network conditions for individual requests using URL match patterns. Unlike the deprecated
2527
+ * Network.emulateNetworkConditions this method does not affect `navigator` state. Use Network.overrideNetworkState to
2528
+ * explicitly modify `navigator` behavior.
2527
2529
  */
2528
2530
  invoke_emulateNetworkConditionsByRule(params: Protocol.Network.EmulateNetworkConditionsByRuleRequest): Promise<Protocol.Network.EmulateNetworkConditionsByRuleResponse>;
2529
2531
 
@@ -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
  }