chrome-devtools-frontend 1.0.1661063 → 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 (131) hide show
  1. package/.agents/skills/devtools-unicode-escaping/SKILL.md +64 -0
  2. package/.agents/skills/evaluate-ai-css-completion/SKILL.md +19 -9
  3. package/.agents/skills/evaluate-ai-css-completion/scripts/evaluate.js +44 -16
  4. package/.agents/skills/migrate-chromium-test/SKILL.md +32 -2
  5. package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +7 -1
  6. package/docs/ui_engineering.md +75 -42
  7. package/front_end/core/common/SettingRegistration.ts +0 -10
  8. package/front_end/core/common/Settings.ts +69 -3
  9. package/front_end/core/platform/ArrayUtilities.ts +10 -0
  10. package/front_end/core/platform/StringUtilities.ts +38 -6
  11. package/front_end/core/sdk/ServiceWorkerCacheModel.ts +0 -10
  12. package/front_end/core/sdk/StorageBucketsModel.ts +0 -10
  13. package/front_end/core/sdk/sdk-meta.ts +0 -1
  14. package/front_end/entrypoints/inspector_main/OutermostTargetSelector.ts +0 -13
  15. package/front_end/entrypoints/inspector_main/RenderingOptions.ts +8 -3
  16. package/front_end/entrypoints/inspector_main/inspector_main-meta.ts +3 -3
  17. package/front_end/entrypoints/main/MainImpl.ts +7 -2
  18. package/front_end/foundation/README.md +86 -0
  19. package/front_end/foundation/Universe.ts +16 -0
  20. package/front_end/generated/InspectorBackendCommands.ts +6 -19
  21. package/front_end/generated/protocol-mapping.d.ts +0 -53
  22. package/front_end/generated/protocol-proxy-api.d.ts +0 -46
  23. package/front_end/generated/protocol.ts +20 -186
  24. package/front_end/models/ai_assistance/AiAgent2.ts +3 -3
  25. package/front_end/models/ai_assistance/AiConversation.ts +21 -10
  26. package/front_end/models/ai_assistance/AiHistoryStorage.ts +21 -10
  27. package/front_end/models/ai_assistance/ChangeManager.ts +2 -2
  28. package/front_end/models/ai_assistance/ExtensionScope.ts +6 -2
  29. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +1 -1
  30. package/front_end/models/ai_assistance/agents/AiAgent.ts +8 -1
  31. package/front_end/models/ai_assistance/agents/StylingAgent.ts +1 -1
  32. package/front_end/models/badges/UserBadges.ts +31 -17
  33. package/front_end/models/bindings/CompilerScriptMapping.ts +5 -4
  34. package/front_end/models/bindings/ContentProviderBasedProject.ts +2 -3
  35. package/front_end/models/bindings/DebuggerLanguagePlugins.ts +4 -3
  36. package/front_end/models/bindings/DefaultScriptMapping.ts +3 -3
  37. package/front_end/models/bindings/NetworkProject.ts +22 -1
  38. package/front_end/models/bindings/ResourceMapping.ts +4 -3
  39. package/front_end/models/bindings/ResourceScriptMapping.ts +2 -1
  40. package/front_end/models/bindings/SASSSourceMapping.ts +4 -3
  41. package/front_end/models/bindings/StylesSourceMapping.ts +3 -3
  42. package/front_end/models/emulation/DeviceModeModel.ts +67 -5
  43. package/front_end/models/extensions/RecorderExtensionEndpoint.ts +7 -5
  44. package/front_end/models/extensions/RecorderPluginManager.ts +10 -6
  45. package/front_end/models/har/Log.snapshot.txt +193 -0
  46. package/front_end/models/har/Log.ts +3 -2
  47. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +14 -5
  48. package/front_end/models/issues_manager/CheckFormsIssuesTrigger.ts +8 -9
  49. package/front_end/models/issues_manager/RelatedIssue.ts +10 -8
  50. package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
  51. package/front_end/models/logs/RequestResolver.ts +2 -2
  52. package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +0 -5
  53. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +8 -6
  54. package/front_end/models/source_map_scopes/NamesResolver.ts +36 -30
  55. package/front_end/models/stack_trace/StackTrace.ts +17 -0
  56. package/front_end/models/trace/handlers/FramesHandler.ts +4 -7
  57. package/front_end/models/trace/handlers/ModelHandlers.ts +0 -1
  58. package/front_end/models/trace/handlers/RendererHandler.ts +2 -15
  59. package/front_end/models/trace/handlers/Threads.ts +4 -11
  60. package/front_end/models/trace/insights/DOMSize.ts +1 -1
  61. package/front_end/models/workspace/WorkspaceImpl.ts +1 -10
  62. package/front_end/panels/application/ApplicationPanelSidebar.ts +0 -44
  63. package/front_end/panels/application/CookieItemsView.ts +1 -1
  64. package/front_end/panels/application/FrameDetailsView.ts +1 -1
  65. package/front_end/panels/application/IndexedDBModel.ts +0 -10
  66. package/front_end/panels/application/IndexedDBViews.ts +240 -164
  67. package/front_end/panels/application/ServiceWorkersView.ts +494 -411
  68. package/front_end/panels/application/SharedStorageModel.ts +0 -10
  69. package/front_end/panels/application/application.ts +0 -6
  70. package/front_end/panels/application/components/AdsView.ts +23 -0
  71. package/front_end/panels/application/components/SharedStorageAccessGrid.ts +1 -1
  72. package/front_end/panels/application/components/adsView.css +21 -1
  73. package/front_end/panels/application/components/components.ts +0 -2
  74. package/front_end/panels/application/indexedDBViews.css +1 -1
  75. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +1 -1
  76. package/front_end/panels/application/serviceWorkersView.css +40 -4
  77. package/front_end/panels/browser_debugger/XHRBreakpointsSidebarPane.ts +147 -123
  78. package/front_end/panels/common/ExtensionServer.ts +3 -3
  79. package/front_end/panels/console/ConsoleView.ts +1 -1
  80. package/front_end/panels/emulation/DeviceModeView.ts +139 -70
  81. package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
  82. package/front_end/panels/lighthouse/LighthouseProtocolService.ts +53 -12
  83. package/front_end/panels/network/NetworkDataGridNode.ts +43 -1
  84. package/front_end/panels/network/RequestInitiatorView.ts +24 -2
  85. package/front_end/panels/network/RequestPayloadView.ts +77 -39
  86. package/front_end/panels/network/components/ResponseHeaderSection.ts +5 -3
  87. package/front_end/panels/network/networkLogView.css +5 -0
  88. package/front_end/panels/network/requestInitiatorViewTree.css +5 -0
  89. package/front_end/panels/profiler/HeapProfileView.ts +198 -79
  90. package/front_end/panels/profiler/HeapSnapshotView.ts +2 -2
  91. package/front_end/panels/recorder/README.md +1 -2
  92. package/front_end/panels/recorder/RecorderPanel.ts +1749 -17
  93. package/front_end/panels/recorder/recorder.ts +0 -2
  94. package/front_end/panels/settings/AISettingsTab.ts +13 -0
  95. package/front_end/panels/sources/SourcesNavigator.ts +3 -2
  96. package/front_end/panels/sources/SourcesView.ts +0 -17
  97. package/front_end/panels/sources/UISourceCodeFrame.ts +42 -0
  98. package/front_end/panels/sources/sources.ts +0 -2
  99. package/front_end/panels/timeline/CompatibilityTracksAppender.ts +0 -17
  100. package/front_end/panels/timeline/ThreadAppender.ts +0 -106
  101. package/front_end/third_party/chromium/README.chromium +1 -1
  102. package/front_end/ui/components/report_view/ReportView.ts +11 -5
  103. package/front_end/ui/components/report_view/report.css +4 -1
  104. package/front_end/ui/components/spinners/Spinner.ts +29 -32
  105. package/front_end/ui/components/spinners/spinner.css +32 -84
  106. package/front_end/ui/legacy/TextPrompt.ts +49 -1
  107. package/front_end/ui/legacy/Treeoutline.ts +14 -6
  108. package/front_end/ui/legacy/UIUtils.ts +36 -11
  109. package/front_end/ui/legacy/Widget.ts +17 -10
  110. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +2 -2
  111. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +36 -7
  112. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +63 -68
  113. package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +1 -1
  114. package/front_end/ui/legacy/components/object_ui/objectValue.css +2 -1
  115. package/front_end/ui/legacy/inspectorCommon.css +4 -4
  116. package/front_end/ui/legacy/textPrompt.css +10 -0
  117. package/front_end/ui/lit/lit.ts +1 -0
  118. package/front_end/ui/lit/render.ts +44 -10
  119. package/front_end/ui/lit/strip-whitespace.ts +23 -2
  120. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  121. package/package.json +1 -1
  122. package/front_end/models/trace/handlers/AuctionWorkletsHandler.ts +0 -183
  123. package/front_end/panels/application/InterestGroupStorageModel.ts +0 -81
  124. package/front_end/panels/application/InterestGroupStorageView.ts +0 -151
  125. package/front_end/panels/application/InterestGroupTreeElement.ts +0 -65
  126. package/front_end/panels/application/components/InterestGroupAccessGrid.ts +0 -135
  127. package/front_end/panels/application/components/interestGroupAccessGrid.css +0 -27
  128. package/front_end/panels/application/interestGroupStorageView.css +0 -9
  129. package/front_end/panels/recorder/RecorderController.ts +0 -1595
  130. package/front_end/panels/sources/InplaceFormatterEditorAction.ts +0 -149
  131. /package/front_end/panels/recorder/{recorderController.css → recorderPanel.css} +0 -0
@@ -70,8 +70,6 @@ import {
70
70
  type ObjectStore,
71
71
  } from './IndexedDBModel.js';
72
72
  import {IDBDatabaseView, IDBDataView} from './IndexedDBViews.js';
73
- import {Events as InterestGroupModelEvents, InterestGroupStorageModel} from './InterestGroupStorageModel.js';
74
- import {InterestGroupTreeElement} from './InterestGroupTreeElement.js';
75
73
  import {OpenedWindowDetailsView, WorkerDetailsView} from './OpenedWindowDetailsView.js';
76
74
  import type * as PreloadingHelper from './preloading/helper/helper.js';
77
75
  import {
@@ -347,7 +345,6 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
347
345
  sessionStorageListTreeElement: ExpandableApplicationPanelTreeElement;
348
346
  extensionStorageListTreeElement: ExpandableApplicationPanelTreeElement;
349
347
  indexedDBListTreeElement: IndexedDBTreeElement;
350
- interestGroupTreeElement: InterestGroupTreeElement;
351
348
  cookieListTreeElement: ExpandableApplicationPanelTreeElement;
352
349
  trustTokensTreeElement: TrustTokensTreeElement;
353
350
  cacheStorageListTreeElement: ServiceWorkerCacheTreeElement;
@@ -484,9 +481,6 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
484
481
  this.trustTokensTreeElement = new TrustTokensTreeElement(panel);
485
482
  storageTreeElement.appendChild(this.trustTokensTreeElement);
486
483
 
487
- this.interestGroupTreeElement = new InterestGroupTreeElement(panel);
488
- storageTreeElement.appendChild(this.interestGroupTreeElement);
489
-
490
484
  this.sharedStorageListTreeElement = new SharedStorageListTreeElement(panel);
491
485
  storageTreeElement.appendChild(this.sharedStorageListTreeElement);
492
486
 
@@ -582,12 +576,6 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
582
576
  modelRemoved: (model: IndexedDBModel) => this.indexedDBModelRemoved(model),
583
577
  },
584
578
  {scoped: true});
585
- SDK.TargetManager.TargetManager.instance().observeModels(
586
- InterestGroupStorageModel, {
587
- modelAdded: (model: InterestGroupStorageModel) => this.interestGroupModelAdded(model),
588
- modelRemoved: (model: InterestGroupStorageModel) => this.interestGroupModelRemoved(model),
589
- },
590
- {scoped: true});
591
579
  SDK.TargetManager.TargetManager.instance().observeModels(
592
580
  SharedStorageModel, {
593
581
  modelAdded: (model: SharedStorageModel) => this.sharedStorageModelAdded(model).catch(err => {
@@ -627,12 +615,6 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
627
615
 
628
616
  this.target = target;
629
617
 
630
- const interestGroupModel = target.model(InterestGroupStorageModel);
631
- if (interestGroupModel) {
632
- interestGroupModel.addEventListener(
633
- InterestGroupModelEvents.INTEREST_GROUP_ACCESS, this.interestGroupAccess, this);
634
- }
635
-
636
618
  const resourceTreeModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
637
619
  if (!resourceTreeModel) {
638
620
  return;
@@ -660,12 +642,6 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
660
642
  SDK.ResourceTreeModel.Events.WillLoadCachedResources, this.resetWithFrames, this);
661
643
  }
662
644
 
663
- const interestGroupModel = target.model(InterestGroupStorageModel);
664
- if (interestGroupModel) {
665
- interestGroupModel.removeEventListener(
666
- InterestGroupModelEvents.INTEREST_GROUP_ACCESS, this.interestGroupAccess, this);
667
- }
668
-
669
645
  this.resetWithFrames();
670
646
  }
671
647
 
@@ -678,10 +654,6 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
678
654
  SDK.TargetManager.TargetManager.instance())) {
679
655
  this.addCookieDocument(frame);
680
656
  }
681
- const interestGroupModel = this.target?.model(InterestGroupStorageModel);
682
- if (interestGroupModel) {
683
- interestGroupModel.enable();
684
- }
685
657
 
686
658
  this.cacheStorageListTreeElement.initialize();
687
659
  const backgroundServiceModel = this.target?.model(BackgroundServiceModel) || null;
@@ -738,16 +710,6 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
738
710
  this.indexedDBListTreeElement.removeIndexedDBForModel(model);
739
711
  }
740
712
 
741
- private interestGroupModelAdded(model: InterestGroupStorageModel): void {
742
- model.enable();
743
- model.addEventListener(InterestGroupModelEvents.INTEREST_GROUP_ACCESS, this.interestGroupAccess, this);
744
- }
745
-
746
- private interestGroupModelRemoved(model: InterestGroupStorageModel): void {
747
- model.disable();
748
- model.removeEventListener(InterestGroupModelEvents.INTEREST_GROUP_ACCESS, this.interestGroupAccess, this);
749
- }
750
-
751
713
  private async sharedStorageModelAdded(model: SharedStorageModel): Promise<void> {
752
714
  await model.enable();
753
715
  for (const storage of model.storages()) {
@@ -817,7 +779,6 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
817
779
  private reset(): void {
818
780
  this.domains = {};
819
781
  this.cookieListTreeElement.removeChildren();
820
- this.interestGroupTreeElement.clearEvents();
821
782
  this.deviceBoundSessionsModel?.clearVisibleSites();
822
783
  this.deviceBoundSessionsModel?.clearEvents();
823
784
  }
@@ -831,11 +792,6 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
831
792
  this.addCookieDocument(frame);
832
793
  }
833
794
 
834
- private interestGroupAccess(event: Common.EventTarget.EventTargetEvent<Protocol.Storage.InterestGroupAccessedEvent>):
835
- void {
836
- this.interestGroupTreeElement.addEvent(event.data);
837
- }
838
-
839
795
  private addCookieDocument(frame: SDK.ResourceTreeModel.ResourceTreeFrame): void {
840
796
  // In case the current frame was unreachable, show its cookies
841
797
  // instead of the error interstitials because they might help to
@@ -402,7 +402,7 @@ export class CookieItemsView extends UI.Widget.VBox {
402
402
  return true;
403
403
  }
404
404
  if (object instanceof SDK.Cookie.Cookie) {
405
- return IssuesManager.RelatedIssue.hasIssues(object);
405
+ return IssuesManager.RelatedIssue.hasIssues(object, IssuesManager.IssuesManager.IssuesManager.instance());
406
406
  }
407
407
  return false;
408
408
  };
@@ -932,7 +932,7 @@ export class FrameDetailsReportView extends UI.Widget.Widget {
932
932
 
933
933
  #uiSourceCodeForFrame(frame: SDK.ResourceTreeModel.ResourceTreeFrame): Workspace.UISourceCode.UISourceCode|null {
934
934
  for (const project of Workspace.Workspace.WorkspaceImpl.instance().projects()) {
935
- const projectTarget = project.target();
935
+ const projectTarget = Bindings.NetworkProject.NetworkProject.getTargetForProject(project);
936
936
  if (projectTarget && projectTarget === frame.resourceTreeModel().target()) {
937
937
  const uiSourceCode = project.uiSourceCodeForURL(frame.url);
938
938
  if (uiSourceCode) {
@@ -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
  }