chrome-devtools-frontend 1.0.1662289 → 1.0.1662965

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 (67) hide show
  1. package/.agents/skills/devtools-unicode-escaping/SKILL.md +64 -0
  2. package/.agents/skills/migrate-chromium-test/SKILL.md +29 -4
  3. package/front_end/core/common/SettingRegistration.ts +0 -5
  4. package/front_end/core/common/Settings.ts +69 -3
  5. package/front_end/core/platform/StringUtilities.ts +38 -6
  6. package/front_end/core/sdk/ServiceWorkerCacheModel.ts +0 -10
  7. package/front_end/core/sdk/StorageBucketsModel.ts +0 -10
  8. package/front_end/core/sdk/sdk-meta.ts +0 -1
  9. package/front_end/entrypoints/inspector_main/OutermostTargetSelector.ts +0 -13
  10. package/front_end/entrypoints/inspector_main/RenderingOptions.ts +8 -3
  11. package/front_end/entrypoints/inspector_main/inspector_main-meta.ts +3 -3
  12. package/front_end/entrypoints/main/MainImpl.ts +7 -2
  13. package/front_end/foundation/Universe.ts +16 -0
  14. package/front_end/generated/InspectorBackendCommands.ts +2 -14
  15. package/front_end/generated/protocol-mapping.d.ts +0 -53
  16. package/front_end/generated/protocol-proxy-api.d.ts +0 -46
  17. package/front_end/generated/protocol.ts +0 -148
  18. package/front_end/models/ai_assistance/AiAgent2.ts +3 -3
  19. package/front_end/models/ai_assistance/AiConversation.ts +21 -10
  20. package/front_end/models/ai_assistance/AiHistoryStorage.ts +21 -10
  21. package/front_end/models/ai_assistance/ChangeManager.ts +2 -2
  22. package/front_end/models/ai_assistance/ExtensionScope.ts +6 -2
  23. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +1 -1
  24. package/front_end/models/ai_assistance/agents/AiAgent.ts +8 -1
  25. package/front_end/models/ai_assistance/agents/StylingAgent.ts +1 -1
  26. package/front_end/models/badges/UserBadges.ts +31 -17
  27. package/front_end/models/bindings/NetworkProject.ts +16 -4
  28. package/front_end/models/emulation/DeviceModeModel.ts +67 -5
  29. package/front_end/models/extensions/RecorderPluginManager.ts +10 -6
  30. package/front_end/models/har/Log.snapshot.txt +193 -0
  31. package/front_end/models/har/Log.ts +3 -2
  32. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +7 -5
  33. package/front_end/models/source_map_scopes/NamesResolver.ts +36 -30
  34. package/front_end/models/stack_trace/StackTrace.ts +17 -0
  35. package/front_end/panels/application/IndexedDBModel.ts +0 -10
  36. package/front_end/panels/application/IndexedDBViews.ts +240 -164
  37. package/front_end/panels/application/SharedStorageModel.ts +0 -10
  38. package/front_end/panels/application/components/AdsView.ts +23 -0
  39. package/front_end/panels/application/components/SharedStorageAccessGrid.ts +1 -1
  40. package/front_end/panels/application/components/adsView.css +21 -1
  41. package/front_end/panels/application/indexedDBViews.css +1 -1
  42. package/front_end/panels/network/NetworkDataGridNode.ts +43 -1
  43. package/front_end/panels/network/RequestInitiatorView.ts +24 -2
  44. package/front_end/panels/network/networkLogView.css +5 -0
  45. package/front_end/panels/network/requestInitiatorViewTree.css +5 -0
  46. package/front_end/panels/profiler/HeapProfileView.ts +198 -79
  47. package/front_end/panels/recorder/README.md +1 -2
  48. package/front_end/panels/recorder/RecorderPanel.ts +1749 -17
  49. package/front_end/panels/recorder/recorder.ts +0 -2
  50. package/front_end/panels/settings/AISettingsTab.ts +13 -0
  51. package/front_end/third_party/chromium/README.chromium +1 -1
  52. package/front_end/ui/components/spinners/Spinner.ts +29 -32
  53. package/front_end/ui/components/spinners/spinner.css +32 -84
  54. package/front_end/ui/legacy/Treeoutline.ts +14 -6
  55. package/front_end/ui/legacy/UIUtils.ts +25 -10
  56. package/front_end/ui/legacy/Widget.ts +17 -10
  57. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +36 -7
  58. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +63 -68
  59. package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +1 -1
  60. package/front_end/ui/legacy/components/object_ui/objectValue.css +2 -1
  61. package/front_end/ui/legacy/inspectorCommon.css +4 -4
  62. package/front_end/ui/lit/lit.ts +1 -0
  63. package/front_end/ui/lit/render.ts +44 -10
  64. package/front_end/ui/lit/strip-whitespace.ts +23 -2
  65. package/package.json +1 -1
  66. package/front_end/panels/recorder/RecorderController.ts +0 -1759
  67. /package/front_end/panels/recorder/{recorderController.css → recorderPanel.css} +0 -0
@@ -2,7 +2,6 @@
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
 
5
- import * as Common from '../../core/common/common.js';
6
5
  import * as SDK from '../../core/sdk/sdk.js';
7
6
  import * as Protocol from '../../generated/protocol.js';
8
7
  import * as Bindings from '../bindings/bindings.js';
@@ -190,7 +189,11 @@ const enum Punctuation {
190
189
 
191
190
  const resolveDebuggerScope = async(scope: SDK.DebuggerModel.ScopeChainEntry):
192
191
  Promise<{variableMapping: Map<string, string>, thisMapping: string | null}> => {
193
- if (!Common.Settings.Settings.instance().moduleSetting('js-source-maps-enabled').get()) {
192
+ if (!scope.callFrame()
193
+ .debuggerModel.target()
194
+ .targetManager()
195
+ .settings.moduleSetting('js-source-maps-enabled')
196
+ .get()) {
194
197
  return {variableMapping: new Map(), thisMapping: null};
195
198
  }
196
199
  const script = scope.callFrame().script;
@@ -278,16 +281,17 @@ const resolveScope = async(script: SDK.Script.Script, scopeChain: Formatter.Form
278
281
 
279
282
  async function resolveSourceName(
280
283
  script: SDK.Script.Script, sourceMap: SDK.SourceMap.SourceMap, name: string,
281
- position: {lineNumber: number, columnNumber: number}): Promise<string|null> {
284
+ position: {lineNumber: number, columnNumber: number},
285
+ debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding =
286
+ Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()): Promise<string|null> {
282
287
  const ranges = sourceMap.findEntryRanges(position.lineNumber, position.columnNumber);
283
288
  if (!ranges) {
284
289
  return null;
285
290
  }
286
291
  // Extract the underlying text from the compiled code's range and make sure that
287
292
  // it starts with the identifier |name|.
288
- const uiSourceCode =
289
- Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().uiSourceCodeForSourceMapSourceURL(
290
- script.debuggerModel, ranges.sourceURL, script.isContentScript());
293
+ const uiSourceCode = debuggerWorkspaceBinding.uiSourceCodeForSourceMapSourceURL(
294
+ script.debuggerModel, ranges.sourceURL, script.isContentScript());
291
295
  if (!uiSourceCode) {
292
296
  return null;
293
297
  }
@@ -363,24 +367,27 @@ const resolveScope = async(script: SDK.Script.Script, scopeChain: Formatter.Form
363
367
  };
364
368
 
365
369
  export const resolveScopeChain =
366
- async function(callFrame: SDK.DebuggerModel.CallFrame): Promise<SDK.DebuggerModel.ScopeChainEntry[]> {
367
- const {pluginManager} = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
368
- const scopeChain: SDK.DebuggerModel.ScopeChainEntry[]|null|undefined =
369
- await pluginManager.resolveScopeChain(callFrame);
370
- if (scopeChain) {
371
- return scopeChain;
372
- }
370
+ async function(callFrame: SDK.DebuggerModel.CallFrame,
371
+ debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding =
372
+ Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()):
373
+ Promise<SDK.DebuggerModel.ScopeChainEntry[]> {
374
+ const {pluginManager} = debuggerWorkspaceBinding;
375
+ const scopeChain: SDK.DebuggerModel.ScopeChainEntry[]|null|undefined =
376
+ await pluginManager.resolveScopeChain(callFrame);
377
+ if (scopeChain) {
378
+ return scopeChain;
379
+ }
373
380
 
374
- // TODO(crbug.com/465968290): Re-enable creating the scope chain from the source map once:
375
- // 1) We have a flag indicating whether the source map contained variable/binding information.
376
- // 2) We have a chrome feature flag.
381
+ // TODO(crbug.com/465968290): Re-enable creating the scope chain from the source map once:
382
+ // 1) We have a flag indicating whether the source map contained variable/binding information.
383
+ // 2) We have a chrome feature flag.
377
384
 
378
- if (callFrame.script.isWasm()) {
379
- return callFrame.scopeChain();
380
- }
381
- const thisObject = await resolveThisObject(callFrame);
382
- return callFrame.scopeChain().map(scope => new ScopeWithSourceMappedVariables(scope, thisObject));
383
- };
385
+ if (callFrame.script.isWasm()) {
386
+ return callFrame.scopeChain();
387
+ }
388
+ const thisObject = await resolveThisObject(callFrame);
389
+ return callFrame.scopeChain().map(scope => new ScopeWithSourceMappedVariables(scope, thisObject));
390
+ };
384
391
 
385
392
  /**
386
393
  * @returns A mapping from original name -> compiled name. If the orignal name is unavailable (e.g. because the compiled name was
@@ -388,7 +395,7 @@ export const resolveScopeChain =
388
395
  */
389
396
  export const allVariablesInCallFrame =
390
397
  async(callFrame: SDK.DebuggerModel.CallFrame): Promise<Map<string, string|null>> => {
391
- if (!Common.Settings.Settings.instance().moduleSetting('js-source-maps-enabled').get()) {
398
+ if (!callFrame.debuggerModel.target().targetManager().settings.moduleSetting('js-source-maps-enabled').get()) {
392
399
  return new Map<string, string|null>();
393
400
  }
394
401
  const cachedMap = cachedMapByCallFrame.get(callFrame);
@@ -424,13 +431,13 @@ export const allVariablesInCallFrame =
424
431
  export const allVariablesAtPosition =
425
432
  async(location: SDK.DebuggerModel.Location): Promise<Map<string, string|null>> => {
426
433
  const reverseMapping = new Map<string, string|null>();
427
- if (!Common.Settings.Settings.instance().moduleSetting('js-source-maps-enabled').get()) {
428
- return reverseMapping;
429
- }
430
434
  const script = location.script();
431
435
  if (!script) {
432
436
  return reverseMapping;
433
437
  }
438
+ if (!script.debuggerModel.target().targetManager().settings.moduleSetting('js-source-maps-enabled').get()) {
439
+ return reverseMapping;
440
+ }
434
441
 
435
442
  const scopeTreeAndText = await computeScopeTree(script);
436
443
  if (!scopeTreeAndText) {
@@ -727,8 +734,9 @@ export async function resolveDebuggerFrameFunctionName(frame: SDK.DebuggerModel.
727
734
  }
728
735
 
729
736
  export async function resolveProfileFrameFunctionName(
730
- {scriptId, lineNumber, columnNumber}: Partial<Protocol.Runtime.CallFrame>,
731
- target: SDK.Target.Target|null): Promise<string|null> {
737
+ {scriptId, lineNumber, columnNumber}: Partial<Protocol.Runtime.CallFrame>, target: SDK.Target.Target|null,
738
+ debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding =
739
+ Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()): Promise<string|null> {
732
740
  if (!target || lineNumber === undefined || columnNumber === undefined || scriptId === undefined) {
733
741
  return null;
734
742
  }
@@ -738,8 +746,6 @@ export async function resolveProfileFrameFunctionName(
738
746
  if (!debuggerModel || !script) {
739
747
  return null;
740
748
  }
741
-
742
- const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
743
749
  const location = new SDK.DebuggerModel.Location(debuggerModel, scriptId, lineNumber, columnNumber);
744
750
  const functionInfoFromPlugin = await debuggerWorkspaceBinding.pluginManager.getFunctionInfo(script, location);
745
751
  if (functionInfoFromPlugin && 'frames' in functionInfoFromPlugin) {
@@ -4,6 +4,7 @@
4
4
 
5
5
  import type * as Common from '../../core/common/common.js';
6
6
  import type * as SDK from '../../core/sdk/sdk.js';
7
+ import type * as Protocol from '../../generated/protocol.js';
7
8
  import type * as Workspace from '../workspace/workspace.js';
8
9
 
9
10
  export type StackTrace = BaseStackTrace<Fragment>;
@@ -131,3 +132,19 @@ export class DebuggableFrameFlavor {
131
132
  return DebuggableFrameFlavor.#last;
132
133
  }
133
134
  }
135
+
136
+ /**
137
+ * Returns whether the given stack trace originated from a direct console
138
+ * invocation. A console-originated stack trace has exactly one frame with
139
+ * no url and no function name.
140
+ *
141
+ * TODO(crbug.com/40726969): Accept a translated `StackTrace` instead of a raw `Protocol.Runtime.StackTrace`.
142
+ */
143
+ export function isConsoleOriginated(stackTrace: Protocol.Runtime.StackTrace): boolean {
144
+ const callFrames = stackTrace.callFrames;
145
+ if (callFrames.length !== 1) {
146
+ return false;
147
+ }
148
+ const frame = callFrames[0];
149
+ return frame.url === '' && frame.functionName === '';
150
+ }
@@ -423,16 +423,6 @@ export class IndexedDBModel extends SDK.SDKModel.SDKModel<EventTypes> implements
423
423
  cacheStorageContentUpdated(_event: Protocol.Storage.CacheStorageContentUpdatedEvent): void {
424
424
  }
425
425
 
426
- interestGroupAccessed(_event: Protocol.Storage.InterestGroupAccessedEvent): void {
427
- }
428
-
429
- interestGroupAuctionEventOccurred(_event: Protocol.Storage.InterestGroupAuctionEventOccurredEvent): void {
430
- }
431
-
432
- interestGroupAuctionNetworkRequestCreated(_event: Protocol.Storage.InterestGroupAuctionNetworkRequestCreatedEvent):
433
- void {
434
- }
435
-
436
426
  sharedStorageAccessed(_event: Protocol.Storage.SharedStorageAccessedEvent): void {
437
427
  }
438
428
 
@@ -1,7 +1,6 @@
1
1
  // Copyright 2012 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 @devtools/no-imperative-dom-api */
5
4
 
6
5
  import '../../ui/components/report_view/report_view.js';
7
6
  import '../../ui/legacy/legacy.js';
@@ -234,6 +233,178 @@ declare global {
234
233
  'devtools-idb-database-view': IDBDatabaseView;
235
234
  }
236
235
  }
236
+ export interface IndexedDBDataViewInput {
237
+ isIndex: boolean;
238
+ index: Index|null;
239
+ objectStore: ObjectStore;
240
+ entries: Entry[];
241
+ skipCount: number;
242
+ selectedRowNumber: number;
243
+ clearButtonEnabled: boolean;
244
+ hasMore: boolean;
245
+ keyFilter: string;
246
+ needsRefreshVisible: boolean;
247
+ metadata: ObjectStoreMetadata|null;
248
+
249
+ refreshButtonClicked: () => void;
250
+ clearButtonClicked: () => Promise<void>;
251
+ deleteButtonClicked: () => Promise<void>;
252
+ pageBackButtonClicked: () => void;
253
+ pageForwardButtonClicked: () => void;
254
+ onKeyFilterChange: (value: string) => void;
255
+ onRowSelected: (rowNumber: number) => void;
256
+ deleteEntry: (entry: Entry) => Promise<void>;
257
+ populateContextMenu: (e: CustomEvent<UI.ContextMenu.ContextMenu>, entry: Entry) => void;
258
+ }
259
+
260
+ const renderKeyPathString = (keyPathString: string): LitTemplate => {
261
+ return html`"<span class="source-code indexed-db-key-path">${keyPathString}</span>"`;
262
+ };
263
+
264
+ const renderKeyColumnHeader = (prefix: string, keyPath: string|string[]|null|undefined): LitTemplate => {
265
+ if (keyPath === undefined || keyPath === null || keyPath === '') {
266
+ return html`${prefix}`;
267
+ }
268
+ return html`
269
+ ${prefix} (${i18nString(UIStrings.keyPath)}${
270
+ Array.isArray(keyPath) ?
271
+ html`[${keyPath.map((path, i) => html`${i > 0 ? ', ' : ''}${renderKeyPathString(path)}`)}]` :
272
+ renderKeyPathString(keyPath)})`;
273
+ };
274
+
275
+ const renderDataGrid = (input: IndexedDBDataViewInput): LitTemplate => {
276
+ const keyPath = input.isIndex && input.index ? input.index.keyPath : input.objectStore.keyPath;
277
+ // clang-format off
278
+ return html`<devtools-data-grid striped style="flex: auto;" name=${i18nString(UIStrings.indexedDb)} .template=${html`
279
+ <style>${indexedDBViewsStyles}</style>
280
+ <table>
281
+ <tr>
282
+ <th id="number" fixed width="50px">#</th>
283
+ <th id="key">${renderKeyColumnHeader(i18nString(UIStrings.keyString), keyPath)}</th>
284
+ ${input.isIndex ? html`<th id="primary-key">${renderKeyColumnHeader(i18nString(UIStrings.primaryKey), input.objectStore.keyPath)}</th>` : nothing}
285
+ <th id="value">${i18nString(UIStrings.valueString)}</th>
286
+ </tr>
287
+ ${repeat(input.entries, (_entry, index) => index, (entry, index) => html`
288
+ <tr ?selected=${index + input.skipCount === input.selectedRowNumber}
289
+ @select=${() => input.onRowSelected(index + input.skipCount)}
290
+ @delete=${() => input.deleteEntry(entry)}
291
+ @contextmenu=${(e: CustomEvent<UI.ContextMenu.ContextMenu>) => input.populateContextMenu(e, entry)}>
292
+ <td>${index + input.skipCount}</td>
293
+ <td>${widget(ObjectPropertiesSectionWidget, {value: entry.key})}</td>
294
+ ${input.isIndex ? html`<td>${widget(ObjectPropertiesSectionWidget, {value: entry.primaryKey})}</td>` : nothing}
295
+ <td class="value-column">${widget(ObjectPropertiesSectionWidget, {value: entry.value})}</td>
296
+ </tr>`
297
+ )}
298
+ </table>`}>
299
+ </devtools-data-grid>`;
300
+ // clang-format on
301
+ };
302
+
303
+ const renderToolbar = (input: IndexedDBDataViewInput): LitTemplate => {
304
+ // clang-format off
305
+ return html`
306
+ <devtools-toolbar class="data-view-toolbar" jslog=${VisualLogging.toolbar()}>
307
+ <devtools-button
308
+ class="toolbar-button"
309
+ .iconName=${'refresh'}
310
+ .title=${i18nString(UIStrings.refresh)}
311
+ jslog=${VisualLogging.action('refresh').track({click: true})}
312
+ @click=${input.refreshButtonClicked}
313
+ .variant=${Buttons.Button.Variant.TOOLBAR}
314
+ ></devtools-button>
315
+ <devtools-button
316
+ class="toolbar-button"
317
+ .iconName=${'clear'}
318
+ .title=${i18nString(UIStrings.clearObjectStore)}
319
+ jslog=${VisualLogging.action('clear-all').track({click: true})}
320
+ @click=${input.clearButtonClicked}
321
+ .disabled=${input.isIndex || !input.clearButtonEnabled}
322
+ .variant=${Buttons.Button.Variant.TOOLBAR}>
323
+ </devtools-button>
324
+ <devtools-button
325
+ class="toolbar-button"
326
+ .iconName=${'bin'}
327
+ .title=${i18nString(UIStrings.deleteSelected)}
328
+ jslog=${VisualLogging.action('delete-selected').track({click: true})}
329
+ @click=${input.deleteButtonClicked}
330
+ .disabled=${input.selectedRowNumber < 0 || input.entries.length === 0}
331
+ .variant=${Buttons.Button.Variant.TOOLBAR}>
332
+ </devtools-button>
333
+
334
+ <div class="toolbar-divider"></div>
335
+
336
+ <devtools-button
337
+ class="toolbar-button"
338
+ .iconName=${'triangle-left'}
339
+ .title=${i18nString(UIStrings.showPreviousPage)}
340
+ .disabled=${input.skipCount <= 0}
341
+ @click=${input.pageBackButtonClicked}
342
+ .variant=${Buttons.Button.Variant.TOOLBAR}>
343
+ </devtools-button>
344
+ <devtools-button
345
+ class="toolbar-button"
346
+ .iconName=${'triangle-right'}
347
+ .title=${i18nString(UIStrings.showNextPage)}
348
+ .disabled=${!input.hasMore}
349
+ @click=${input.pageForwardButtonClicked}
350
+ .variant=${Buttons.Button.Variant.TOOLBAR}>
351
+ </devtools-button>
352
+
353
+ <devtools-toolbar-input
354
+ type="filter"
355
+ placeholder=${i18nString(UIStrings.filterByKey)}
356
+ class="key-filter-input"
357
+ .value=${input.keyFilter}
358
+ @change=${(e: CustomEvent<string>) => {
359
+ input.onKeyFilterChange(e.detail);
360
+ }}>
361
+ </devtools-toolbar-input>
362
+
363
+ ${input.needsRefreshVisible ? html`
364
+ <div class="toolbar-divider"></div>
365
+ <div class="toolbar-item stale-data-warning" title=${
366
+ i18nString(
367
+ UIStrings
368
+ .someEntriesMayHaveBeenModified)}>
369
+ <devtools-icon name="warning" class="warning-icon"></devtools-icon>
370
+ <span>${i18nString(UIStrings.dataMayBeStale)}</span>
371
+ </div>
372
+ ` : nothing}
373
+ </devtools-toolbar>`;
374
+ // clang-format on
375
+ };
376
+
377
+ const renderSummaryBar = (input: IndexedDBDataViewInput): LitTemplate => {
378
+ const metadata = input.metadata;
379
+ if (!metadata) {
380
+ return nothing;
381
+ }
382
+ // clang-format off
383
+ return html`
384
+ <div class="object-store-summary-bar">
385
+ <span>${i18nString(UIStrings.totalEntriesS, { PH1: String(metadata.entriesCount)})}</span>
386
+ ${input.objectStore.autoIncrement ? html`
387
+ <span class="separator">\u2758</span>
388
+ <span>${i18nString(UIStrings.keyGeneratorValueS, {PH1: String(metadata.keyGeneratorValue)})}</span>`
389
+ : nothing}
390
+ </div>`;
391
+ // clang-format on
392
+ };
393
+
394
+ export type IDBDataViewView = (input: IndexedDBDataViewInput, output: undefined, target: HTMLElement) => void;
395
+
396
+ // clang-format off
397
+ export const IDB_DATA_VIEW_DEFAULT_VIEW: IDBDataViewView = (input, _output, target) => {
398
+ render(html`
399
+ ${renderToolbar(input)}
400
+ <div class="data-grid-container">
401
+ ${renderDataGrid(input)}
402
+ </div>
403
+ ${renderSummaryBar(input)}
404
+ `, target, {container: {classes: ['indexed-db-data-view', 'storage-view']}});
405
+ };
406
+ // clang-format on
407
+
237
408
  export class IDBDataView extends UI.View.SimpleView {
238
409
  private readonly model: IndexedDBModel;
239
410
  private readonly databaseId: DatabaseId;
@@ -259,14 +430,16 @@ export class IDBDataView extends UI.View.SimpleView {
259
430
  // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
260
431
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
261
432
  private lastKey?: any;
433
+ readonly #view: IDBDataViewView;
262
434
 
263
435
  constructor(model: IndexedDBModel, databaseId: DatabaseId, objectStore: ObjectStore, index: Index|null,
264
- refreshObjectStoreCallback: () => void) {
436
+ refreshObjectStoreCallback: () => void, view = IDB_DATA_VIEW_DEFAULT_VIEW) {
265
437
  super({
266
438
  title: i18nString(UIStrings.idb),
267
439
  viewId: 'idb',
268
440
  jslog: `${VisualLogging.pane('indexed-db-data-view')}`,
269
441
  });
442
+ this.#view = view;
270
443
  this.registerRequiredCSS(indexedDBViewsStyles);
271
444
  this.registerRequiredCSS(DataGrid.dataGridStyles);
272
445
 
@@ -275,8 +448,6 @@ export class IDBDataView extends UI.View.SimpleView {
275
448
  this.isIndex = Boolean(index);
276
449
  this.refreshObjectStoreCallback = refreshObjectStoreCallback;
277
450
 
278
- this.element.classList.add('indexed-db-data-view', 'storage-view');
279
-
280
451
  this.clearingObjectStore = false;
281
452
 
282
453
  this.pageSize = 50;
@@ -286,124 +457,6 @@ export class IDBDataView extends UI.View.SimpleView {
286
457
  this.update(objectStore, index);
287
458
  }
288
459
 
289
- private renderDataGrid(): LitTemplate {
290
- const keyPath = this.isIndex && this.index ? this.index.keyPath : this.objectStore.keyPath;
291
- // clang-format off
292
- return html`<devtools-data-grid striped style="flex: auto;" name=${i18nString(UIStrings.indexedDb)} .template=${html`
293
- <style>${indexedDBViewsStyles}</style>
294
- <table>
295
- <tr>
296
- <th id="number" fixed width="50px">#</th>
297
- <th id="key">${this.renderKeyColumnHeader(i18nString(UIStrings.keyString), keyPath)}</th>
298
- ${this.isIndex ? html`<th id="primary-key">${this.renderKeyColumnHeader(i18nString(UIStrings.primaryKey), this.objectStore.keyPath)}</th>` : nothing}
299
- <th id="value">${i18nString(UIStrings.valueString)}</th>
300
- </tr>
301
- ${repeat(this.entries, (_entry, index) => index, (entry, index) => html`
302
- <tr ?selected=${index + this.skipCount === this.#selectedRowNumber}
303
- @select=${() => this.onRowSelected(index + this.skipCount)}
304
- @delete=${() => this.deleteEntry(entry)}
305
- @contextmenu=${(e: CustomEvent<UI.ContextMenu.ContextMenu>) => this.populateContextMenu(e, entry)}>
306
- <td>${index + this.skipCount}</td>
307
- <td>${widget(ObjectPropertiesSectionWidget, {value: entry.key})}</td>
308
- ${this.isIndex ? html`<td>${widget(ObjectPropertiesSectionWidget, {value: entry.primaryKey})}</td>` : nothing}
309
- <td class="value-column">${widget(ObjectPropertiesSectionWidget, {value: entry.value})}</td>
310
- </tr>`
311
- )}
312
- </table>`}>
313
- </devtools-data-grid>`;
314
- // clang-format on
315
- }
316
-
317
- private renderKeyColumnHeader(prefix: string, keyPath: string|string[]|null|undefined): LitTemplate {
318
- if (keyPath === undefined || keyPath === null || keyPath === '') {
319
- return html`${prefix}`;
320
- }
321
- return html`
322
- ${prefix} (${i18nString(UIStrings.keyPath)}${
323
- Array.isArray(keyPath) ?
324
- html`[${keyPath.map((path, i) => html`${i > 0 ? ', ' : ''}${this.renderKeyPathString(path)}`)}]` :
325
- this.renderKeyPathString(keyPath)})`;
326
- }
327
-
328
- private renderKeyPathString(keyPathString: string): LitTemplate {
329
- return html`"<span class="source-code indexed-db-key-path">${keyPathString}</span>"`;
330
- }
331
-
332
- private renderToolbar(): LitTemplate {
333
- // clang-format off
334
- return html`
335
- <devtools-toolbar class="data-view-toolbar" jslog=${VisualLogging.toolbar()}>
336
- <devtools-button
337
- class="toolbar-button"
338
- .iconName=${'refresh'}
339
- .title=${i18nString(UIStrings.refresh)}
340
- jslog=${VisualLogging.action('refresh').track({click: true})}
341
- @click=${() => this.refreshButtonClicked()}
342
- .variant=${Buttons.Button.Variant.TOOLBAR}
343
- ></devtools-button>
344
- <devtools-button
345
- class="toolbar-button"
346
- .iconName=${'clear'}
347
- .title=${i18nString(UIStrings.clearObjectStore)}
348
- jslog=${VisualLogging.action('clear-all').track({click: true})}
349
- @click=${() => this.clearButtonClicked()}
350
- .disabled=${this.isIndex || !this.#clearButtonEnabled}
351
- .variant=${Buttons.Button.Variant.TOOLBAR}>
352
- </devtools-button>
353
- <devtools-button
354
- class="toolbar-button"
355
- .iconName=${'bin'}
356
- .title=${i18nString(UIStrings.deleteSelected)}
357
- jslog=${VisualLogging.action('delete-selected').track({click: true})}
358
- @click=${() => this.deleteButtonClicked()}
359
- .disabled=${this.#selectedRowNumber < 0 || this.entries.length === 0}
360
- .variant=${Buttons.Button.Variant.TOOLBAR}>
361
- </devtools-button>
362
-
363
- <div class="toolbar-divider"></div>
364
-
365
- <devtools-button
366
- class="toolbar-button"
367
- .iconName=${'triangle-left'}
368
- .title=${i18nString(UIStrings.showPreviousPage)}
369
- .disabled=${this.skipCount <= 0}
370
- @click=${() => this.pageBackButtonClicked()}
371
- .variant=${Buttons.Button.Variant.TOOLBAR}>
372
- </devtools-button>
373
- <devtools-button
374
- class="toolbar-button"
375
- .iconName=${'triangle-right'}
376
- .title=${i18nString(UIStrings.showNextPage)}
377
- .disabled=${!this.#hasMore}
378
- @click=${() => this.pageForwardButtonClicked()}
379
- .variant=${Buttons.Button.Variant.TOOLBAR}>
380
- </devtools-button>
381
-
382
- <devtools-toolbar-input
383
- type="filter"
384
- placeholder=${i18nString(UIStrings.filterByKey)}
385
- class="key-filter-input"
386
- .value=${this.#keyFilter}
387
- @change=${(e: CustomEvent<string>) => {
388
- this.#keyFilter = e.detail;
389
- this.updateData(false);
390
- }}>
391
- </devtools-toolbar-input>
392
-
393
- ${this.#needsRefreshVisible ? html`
394
- <div class="toolbar-divider"></div>
395
- <div class="toolbar-item stale-data-warning" title=${
396
- i18nString(
397
- UIStrings
398
- .someEntriesMayHaveBeenModified)}>
399
- <devtools-icon name="warning" class="warning-icon"></devtools-icon>
400
- <span>${i18nString(UIStrings.dataMayBeStale)}</span>
401
- </div>
402
- ` : nothing}
403
- </devtools-toolbar>`;
404
- // clang-format on
405
- }
406
-
407
460
  private pageBackButtonClicked(): void {
408
461
  this.skipCount = Math.max(0, this.skipCount - this.pageSize);
409
462
  this.updateData(false);
@@ -519,23 +572,6 @@ export class IDBDataView extends UI.View.SimpleView {
519
572
  });
520
573
  }
521
574
 
522
- private renderSummaryBar(): LitTemplate {
523
- const metadata = this.#metadata;
524
- if (!metadata) {
525
- return nothing;
526
- }
527
- // clang-format off
528
- return html`
529
- <div class="object-store-summary-bar">
530
- <span>${i18nString(UIStrings.totalEntriesS, { PH1: String(metadata.entriesCount)})}</span>
531
- ${this.objectStore.autoIncrement ? html`
532
- <span class="separator">\u2758</span>
533
- <span>${i18nString(UIStrings.keyGeneratorValueS, {PH1: String(metadata.keyGeneratorValue)})}</span>`
534
- : nothing}
535
- </div>`;
536
- // clang-format on
537
- }
538
-
539
575
  private updatedDataForTests(): void {
540
576
  // Sniffed in tests.
541
577
  }
@@ -548,6 +584,8 @@ export class IDBDataView extends UI.View.SimpleView {
548
584
  const ok =
549
585
  await UI.UIUtils.ConfirmDialog.show(i18nString(UIStrings.objectStoreWillBeCleared),
550
586
  i18nString(UIStrings.confirmClearObjectStore, {PH1: this.objectStore.name}),
587
+ // TODO(b/407750537): Fix the linter false positive
588
+ // eslint-disable-next-line @devtools/no-imperative-dom-api
551
589
  this.element, {jslogContext: 'clear-object-store-confirmation'});
552
590
  if (ok) {
553
591
  this.#clearButtonEnabled = false;
@@ -622,22 +660,72 @@ export class IDBDataView extends UI.View.SimpleView {
622
660
  }
623
661
 
624
662
  override performUpdate(): void {
625
- // clang-format off
626
- // eslint-disable-next-line @devtools/no-lit-render-outside-of-view
627
- render(html`
628
- ${this.renderToolbar()}
629
- <div class="data-grid-container">
630
- ${this.renderDataGrid()}
631
- </div>
632
- ${this.renderSummaryBar()}
633
- `, this.element);
634
- // clang-format on
663
+ this.#view({
664
+ isIndex: this.isIndex,
665
+ index: this.index,
666
+ objectStore: this.objectStore,
667
+ entries: this.entries,
668
+ skipCount: this.skipCount,
669
+ selectedRowNumber: this.#selectedRowNumber,
670
+ clearButtonEnabled: this.#clearButtonEnabled,
671
+ hasMore: this.#hasMore,
672
+ keyFilter: this.#keyFilter,
673
+ needsRefreshVisible: this.#needsRefreshVisible,
674
+ metadata: this.#metadata,
675
+ refreshButtonClicked: this.refreshButtonClicked.bind(this),
676
+ clearButtonClicked: this.clearButtonClicked.bind(this),
677
+ deleteButtonClicked: this.deleteButtonClicked.bind(this),
678
+ pageBackButtonClicked: this.pageBackButtonClicked.bind(this),
679
+ pageForwardButtonClicked: this.pageForwardButtonClicked.bind(this),
680
+ onKeyFilterChange: (value: string) => {
681
+ this.#keyFilter = value;
682
+ this.updateData(false);
683
+ },
684
+ onRowSelected: this.onRowSelected.bind(this),
685
+ deleteEntry: this.deleteEntry.bind(this),
686
+ populateContextMenu: this.populateContextMenu.bind(this),
687
+ },
688
+ undefined, this.element);
635
689
  }
636
690
  }
637
691
 
692
+ interface ObjectPropertiesSectionWidgetInput {
693
+ value: SDK.RemoteObject.RemoteObject|null;
694
+ }
695
+
696
+ interface ObjectPropertiesSectionWidgetOutput {
697
+ objectPropSection: ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection|null;
698
+ }
699
+
700
+ type ObjectPropertiesSectionWidgetView = (
701
+ input: ObjectPropertiesSectionWidgetInput,
702
+ output: ObjectPropertiesSectionWidgetOutput,
703
+ target: HTMLElement,
704
+ ) => void;
705
+
706
+ const OBJECT_PROPERTIES_SECTION_WIDGET_DEFAULT_VIEW: ObjectPropertiesSectionWidgetView = (input, output, target) => {
707
+ if (!input.value) {
708
+ output.objectPropSection = null;
709
+ render(nothing, target);
710
+ return;
711
+ }
712
+ const objectPropSection = ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection.defaultObjectPropertiesSection(
713
+ input.value, undefined /* linkifier */, true /* skipProto */, true /* readOnly */);
714
+ output.objectPropSection = objectPropSection;
715
+
716
+ const element = input.value.hasChildren ? objectPropSection.element : objectPropSection.titleElement;
717
+ render(html`${element}`, target);
718
+ };
719
+
638
720
  class ObjectPropertiesSectionWidget extends UI.Widget.Widget {
639
721
  #value: SDK.RemoteObject.RemoteObject|null = null;
640
722
  #objectPropSection: ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection|null = null;
723
+ readonly #view: ObjectPropertiesSectionWidgetView;
724
+
725
+ constructor(element?: HTMLElement, view = OBJECT_PROPERTIES_SECTION_WIDGET_DEFAULT_VIEW) {
726
+ super(element);
727
+ this.#view = view;
728
+ }
641
729
 
642
730
  set value(value: SDK.RemoteObject.RemoteObject|null) {
643
731
  if (this.#value === value) {
@@ -652,20 +740,8 @@ class ObjectPropertiesSectionWidget extends UI.Widget.Widget {
652
740
  }
653
741
 
654
742
  override performUpdate(): void {
655
- const value = this.#value;
656
- if (!value) {
657
- this.contentElement.removeChildren();
658
- this.#objectPropSection = null;
659
- return;
660
- }
661
- this.contentElement.removeChildren();
662
- this.#objectPropSection = ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection.defaultObjectPropertiesSection(
663
- value, undefined /* linkifier */, true /* skipProto */, true /* readOnly */);
664
-
665
- if (value.hasChildren) {
666
- this.contentElement.appendChild(this.#objectPropSection.element);
667
- } else {
668
- this.contentElement.appendChild(this.#objectPropSection.titleElement);
669
- }
743
+ const output: ObjectPropertiesSectionWidgetOutput = {objectPropSection: null};
744
+ this.#view({value: this.#value}, output, this.contentElement);
745
+ this.#objectPropSection = output.objectPropSection;
670
746
  }
671
747
  }
@@ -227,16 +227,6 @@ export class SharedStorageModel extends SDK.SDKModel.SDKModel<EventTypes> implem
227
227
  cacheStorageContentUpdated(_event: Protocol.Storage.CacheStorageContentUpdatedEvent): void {
228
228
  }
229
229
 
230
- interestGroupAccessed(_event: Protocol.Storage.InterestGroupAccessedEvent): void {
231
- }
232
-
233
- interestGroupAuctionEventOccurred(_event: Protocol.Storage.InterestGroupAuctionEventOccurredEvent): void {
234
- }
235
-
236
- interestGroupAuctionNetworkRequestCreated(_event: Protocol.Storage.InterestGroupAuctionNetworkRequestCreatedEvent):
237
- void {
238
- }
239
-
240
230
  storageBucketCreatedOrUpdated(_event: Protocol.Storage.StorageBucketCreatedOrUpdatedEvent): void {
241
231
  }
242
232