chrome-devtools-frontend 1.0.1553956 → 1.0.1555430

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 (85) hide show
  1. package/front_end/Images/src/spark.svg +10 -0
  2. package/front_end/core/protocol_client/InspectorBackend.ts +1 -1
  3. package/front_end/core/root/Runtime.ts +0 -4
  4. package/front_end/core/sdk/DOMModel.ts +101 -7
  5. package/front_end/core/sdk/ResourceTreeModel.ts +0 -1
  6. package/front_end/generated/SupportedCSSProperties.js +18 -0
  7. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +1 -1
  8. package/front_end/models/ai_assistance/agents/StylingAgent.ts +1 -1
  9. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +1 -1
  10. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -1
  11. package/front_end/{ui/components → models}/annotations/AnnotationRepository.ts +3 -3
  12. package/front_end/models/annotations/README.md +7 -0
  13. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +8 -0
  14. package/front_end/models/javascript_metadata/NativeFunctions.js +15 -23
  15. package/front_end/models/stack_trace/StackTrace.ts +13 -2
  16. package/front_end/models/stack_trace/StackTraceImpl.ts +81 -6
  17. package/front_end/models/stack_trace/StackTraceModel.ts +35 -3
  18. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +45 -4
  19. package/front_end/panels/ai_assistance/components/ArtifactsViewer.ts +57 -0
  20. package/front_end/panels/ai_assistance/components/ChatView.ts +1 -0
  21. package/front_end/panels/ai_assistance/components/artifactsViewer.css +10 -0
  22. package/front_end/panels/application/BounceTrackingMitigationsTreeElement.ts +2 -6
  23. package/front_end/panels/application/components/BounceTrackingMitigationsView.ts +133 -118
  24. package/front_end/panels/application/preloading/PreloadingView.ts +12 -6
  25. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +230 -237
  26. package/front_end/panels/application/preloading/components/PreloadingGrid.ts +96 -79
  27. package/front_end/panels/application/preloading/components/preloadingGrid.css +26 -29
  28. package/front_end/panels/application/preloading/preloadingView.css +6 -0
  29. package/front_end/panels/common/Annotation.ts +1 -1
  30. package/front_end/panels/common/AnnotationManager.ts +1 -1
  31. package/front_end/panels/common/ExtensionView.ts +1 -0
  32. package/front_end/panels/console/ConsoleContextSelector.ts +74 -9
  33. package/front_end/panels/console/consoleContextSelector.css +31 -29
  34. package/front_end/panels/coverage/coverageListView.css +59 -57
  35. package/front_end/panels/elements/ElementsPanel.ts +1 -1
  36. package/front_end/panels/elements/ElementsTreeElement.ts +39 -1
  37. package/front_end/panels/elements/ElementsTreeOutline.ts +23 -21
  38. package/front_end/panels/elements/TopLayerContainer.ts +26 -91
  39. package/front_end/panels/explain/components/ConsoleInsight.ts +3 -3
  40. package/front_end/panels/network/NetworkItemView.ts +1 -1
  41. package/front_end/panels/network/NetworkLogView.ts +1 -1
  42. package/front_end/panels/network/NetworkPanel.ts +1 -1
  43. package/front_end/panels/recorder/RecorderController.ts +0 -1
  44. package/front_end/panels/security/CookieControlsView.ts +21 -10
  45. package/front_end/panels/security/SecurityPanelSidebar.ts +5 -0
  46. package/front_end/panels/timeline/CompatibilityTracksAppender.ts +0 -1
  47. package/front_end/panels/timeline/TimelineUIUtils.ts +5 -8
  48. package/front_end/panels/timeline/components/Sidebar.ts +16 -7
  49. package/front_end/panels/timeline/components/SidebarInsightsTab.ts +169 -129
  50. package/front_end/panels/timeline/components/TimelineSummary.ts +75 -54
  51. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +16 -25
  52. package/front_end/panels/timeline/components/insights/Cache.ts +12 -8
  53. package/front_end/panels/timeline/components/insights/Checklist.ts +53 -43
  54. package/front_end/panels/timeline/components/insights/DOMSize.ts +25 -21
  55. package/front_end/panels/timeline/components/insights/DocumentLatency.ts +6 -3
  56. package/front_end/panels/timeline/components/insights/DuplicatedJavaScript.ts +7 -7
  57. package/front_end/panels/timeline/components/insights/FontDisplay.ts +7 -5
  58. package/front_end/panels/timeline/components/insights/ForcedReflow.ts +11 -9
  59. package/front_end/panels/timeline/components/insights/INPBreakdown.ts +7 -6
  60. package/front_end/panels/timeline/components/insights/ImageDelivery.ts +7 -5
  61. package/front_end/panels/timeline/components/insights/InsightRenderer.ts +20 -18
  62. package/front_end/panels/timeline/components/insights/LCPBreakdown.ts +12 -12
  63. package/front_end/panels/timeline/components/insights/LCPDiscovery.ts +7 -3
  64. package/front_end/panels/timeline/components/insights/LegacyJavaScript.ts +7 -7
  65. package/front_end/panels/timeline/components/insights/ModernHTTP.ts +7 -5
  66. package/front_end/panels/timeline/components/insights/NetworkDependencyTree.ts +15 -13
  67. package/front_end/panels/timeline/components/insights/RenderBlocking.ts +2 -2
  68. package/front_end/panels/timeline/components/insights/SlowCSSSelector.ts +15 -14
  69. package/front_end/panels/timeline/components/insights/Table.ts +152 -130
  70. package/front_end/panels/timeline/components/insights/ThirdParties.ts +11 -9
  71. package/front_end/panels/timeline/components/sidebarInsightsTab.css +50 -48
  72. package/front_end/panels/timeline/components/timelineSummary.css +58 -57
  73. package/front_end/panels/timeline/thirdPartyTreeView.css +109 -0
  74. package/front_end/panels/timeline/timelineDetailsView.css +2 -4
  75. package/front_end/panels/timeline/timelinePanel.css +0 -110
  76. package/front_end/third_party/chromium/README.chromium +1 -1
  77. package/front_end/ui/components/settings/SettingCheckbox.ts +4 -6
  78. package/front_end/ui/legacy/TabbedPane.ts +20 -13
  79. package/front_end/ui/legacy/ViewManager.ts +2 -32
  80. package/front_end/ui/legacy/Widget.ts +1 -3
  81. package/front_end/ui/legacy/tabbedPane.css +4 -7
  82. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  83. package/package.json +1 -1
  84. /package/front_end/{ui/components → models}/annotations/AnnotationType.ts +0 -0
  85. /package/front_end/{ui/components → models}/annotations/annotations.ts +0 -0
@@ -7,7 +7,14 @@ import type * as Protocol from '../../generated/protocol.js';
7
7
 
8
8
  // eslint-disable-next-line @devtools/es-modules-import
9
9
  import * as StackTrace from './stack_trace.js';
10
- import {AsyncFragmentImpl, FragmentImpl, FrameImpl, StackTraceImpl} from './StackTraceImpl.js';
10
+ import {
11
+ type AnyStackTraceImpl,
12
+ AsyncFragmentImpl,
13
+ DebuggableFragmentImpl,
14
+ FragmentImpl,
15
+ FrameImpl,
16
+ StackTraceImpl
17
+ } from './StackTraceImpl.js';
11
18
  import {type FrameNode, type RawFrame, Trie} from './Trie.js';
12
19
 
13
20
  /**
@@ -45,10 +52,21 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
45
52
  return new StackTraceImpl(syncFragment, asyncFragments);
46
53
  }
47
54
 
55
+ async createFromDebuggerPaused(
56
+ pausedDetails: SDK.DebuggerModel.DebuggerPausedDetails,
57
+ rawFramesToUIFrames: TranslateRawFrames): Promise<StackTrace.StackTrace.DebuggableStackTrace> {
58
+ const [syncFragment, asyncFragments] = await Promise.all([
59
+ this.#createDebuggableFragment(pausedDetails, rawFramesToUIFrames),
60
+ this.#createAsyncFragments(pausedDetails, rawFramesToUIFrames),
61
+ ]);
62
+
63
+ return new StackTraceImpl(syncFragment, asyncFragments);
64
+ }
65
+
48
66
  /** Trigger re-translation of all fragments with the provide script in their call stack */
49
67
  async scriptInfoChanged(script: SDK.Script.Script, translateRawFrames: TranslateRawFrames): Promise<void> {
50
68
  const translatePromises: Array<Promise<unknown>> = [];
51
- let stackTracesToUpdate = new Set<StackTraceImpl>();
69
+ let stackTracesToUpdate = new Set<AnyStackTraceImpl>();
52
70
 
53
71
  for (const fragment of this.#affectedFragments(script)) {
54
72
  // We trigger re-translation only for fragments of leaf-nodes. Any fragment along the ancestor-chain
@@ -75,8 +93,22 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
75
93
  return fragment;
76
94
  }
77
95
 
96
+ async #createDebuggableFragment(
97
+ pausedDetails: SDK.DebuggerModel.DebuggerPausedDetails,
98
+ rawFramesToUIFrames: TranslateRawFrames): Promise<DebuggableFragmentImpl> {
99
+ const fragment = this.#createFragment(pausedDetails.callFrames.map(frame => ({
100
+ scriptId: frame.script.scriptId,
101
+ url: frame.script.sourceURL,
102
+ functionName: frame.functionName,
103
+ lineNumber: frame.location().lineNumber,
104
+ columnNumber: frame.location().columnNumber,
105
+ })));
106
+ await this.#translateFragment(fragment, rawFramesToUIFrames);
107
+ return new DebuggableFragmentImpl(fragment, pausedDetails.callFrames);
108
+ }
109
+
78
110
  async #createAsyncFragments(
79
- stackTraceOrPausedEvent: Protocol.Runtime.StackTrace|Protocol.Debugger.PausedEvent,
111
+ stackTraceOrPausedEvent: Protocol.Runtime.StackTrace|SDK.DebuggerModel.DebuggerPausedDetails,
80
112
  rawFramesToUIFrames: TranslateRawFrames): Promise<AsyncFragmentImpl[]> {
81
113
  const asyncFragments: AsyncFragmentImpl[] = [];
82
114
  const translatePromises: Array<Promise<unknown>> = [];
@@ -12,10 +12,10 @@ import * as Root from '../../core/root/root.js';
12
12
  import * as SDK from '../../core/sdk/sdk.js';
13
13
  import * as Protocol from '../../generated/protocol.js';
14
14
  import * as AiAssistanceModel from '../../models/ai_assistance/ai_assistance.js';
15
+ import * as Annotations from '../../models/annotations/annotations.js';
15
16
  import * as Badges from '../../models/badges/badges.js';
16
17
  import * as TextUtils from '../../models/text_utils/text_utils.js';
17
18
  import * as Workspace from '../../models/workspace/workspace.js';
18
- import * as Annotations from '../../ui/components/annotations/annotations.js';
19
19
  import * as Buttons from '../../ui/components/buttons/buttons.js';
20
20
  import * as Snackbars from '../../ui/components/snackbars/snackbars.js';
21
21
  import * as UIHelpers from '../../ui/helpers/helpers.js';
@@ -27,6 +27,7 @@ import * as NetworkPanel from '../network/network.js';
27
27
  import * as TimelinePanel from '../timeline/timeline.js';
28
28
 
29
29
  import aiAssistancePanelStyles from './aiAssistancePanel.css.js';
30
+ import {ArtifactsViewer} from './components/ArtifactsViewer.js';
30
31
  import {
31
32
  type AnswerPart,
32
33
  type ChatMessage,
@@ -294,6 +295,8 @@ interface ToolbarViewInput {
294
295
  onExportConversationClick: () => void;
295
296
  onHelpClick: () => void;
296
297
  onSettingsClick: () => void;
298
+ onArtifactsSidebarToggle: () => void;
299
+ artifactsSidebarVisible: boolean;
297
300
  isLoading: boolean;
298
301
  showChatActions: boolean;
299
302
  showActiveConversationActions: boolean;
@@ -386,6 +389,13 @@ function toolbarView(input: ToolbarViewInput): Lit.LitTemplate {
386
389
  .jslogContext=${'freestyler.settings'}
387
390
  .variant=${Buttons.Button.Variant.TOOLBAR}
388
391
  @click=${input.onSettingsClick}></devtools-button>
392
+ <!-- If the green experiment is enabled, render the artifacts sidebar toggle button -->
393
+ ${Root.Runtime.hostConfig.devToolsGreenDevUi?.enabled ? html`<devtools-button
394
+ title=${i18nString(UIStrings.settings)}
395
+ aria-label=${i18nString(UIStrings.settings)}
396
+ .iconName=${input.artifactsSidebarVisible ? 'left-panel-open' : 'left-panel-close'}
397
+ .variant=${Buttons.Button.Variant.TOOLBAR}
398
+ @click=${input.onArtifactsSidebarToggle}></devtools-button>` : Lit.nothing}
389
399
  </devtools-toolbar>
390
400
  </div>
391
401
  `;
@@ -396,8 +406,9 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
396
406
  // clang-format off
397
407
  function renderState(): Lit.TemplateResult {
398
408
  switch (input.state) {
399
- case ViewState.CHAT_VIEW:
400
- return html`<devtools-ai-chat-view
409
+ case ViewState.CHAT_VIEW: {
410
+ const aiChatView = html`
411
+ <devtools-ai-chat-view
401
412
  .props=${input.props}
402
413
  ${Lit.Directives.ref((el: Element | undefined) => {
403
414
  if (!el || !(el instanceof ChatView)) {
@@ -407,6 +418,30 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
407
418
  output.chatView = el;
408
419
  })}
409
420
  ></devtools-ai-chat-view>`;
421
+ // If the green experiment is enabled, render the chat view inside
422
+ // a split view to also have an artifacts viewer sidebar.
423
+ if(Root.Runtime.hostConfig.devToolsGreenDevUi?.enabled) {
424
+ return html`
425
+ <devtools-split-view
426
+ direction="column"
427
+ sidebar-visibility=${input.props.isArtifactsSidebarOpen ? 'visible' : 'hidden'}
428
+ sidebar-position="second"
429
+ style="width: 100%;"
430
+ >
431
+ <div slot="main">
432
+ ${aiChatView}
433
+ </div>
434
+ <div slot="sidebar">
435
+ <devtools-widget
436
+ class="fill-panel"
437
+ .widgetConfig=${UI.Widget.widgetConfig(ArtifactsViewer)}
438
+ ></devtools-widget>
439
+ </div>
440
+ </devtools-split-view>`;
441
+ }
442
+
443
+ return aiChatView;
444
+ }
410
445
  case ViewState.EXPLORE_VIEW:
411
446
  return html`<devtools-widget
412
447
  class="fill-panel"
@@ -482,7 +517,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
482
517
  #selectedElement: AiAssistanceModel.StylingAgent.NodeContext|null = null;
483
518
  #selectedPerformanceTrace: AiAssistanceModel.PerformanceAgent.PerformanceTraceContext|null = null;
484
519
  #selectedRequest: AiAssistanceModel.NetworkAgent.RequestContext|null = null;
485
-
520
+ #isArtifactsSidebarOpen = false;
486
521
  // Messages displayed in the `ChatView` component.
487
522
  #messages: ChatMessage[] = [];
488
523
 
@@ -574,6 +609,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
574
609
  uploadImageInputEnabled: isAiAssistanceMultimodalUploadInputEnabled() &&
575
610
  this.#conversation.type === AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING,
576
611
  markdownRenderer,
612
+ isArtifactsSidebarOpen: this.#isArtifactsSidebarOpen,
577
613
  onTextSubmit: async (
578
614
  text: string, imageInput?: Host.AidaClient.Part,
579
615
  multimodalInputType?: AiAssistanceModel.AiAgent.MultimodalInputType) => {
@@ -927,6 +963,11 @@ export class AiAssistancePanel extends UI.Panel.Panel {
927
963
  onSettingsClick: () => {
928
964
  void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
929
965
  },
966
+ onArtifactsSidebarToggle: () => {
967
+ this.#isArtifactsSidebarOpen = !this.#isArtifactsSidebarOpen;
968
+ this.requestUpdate();
969
+ },
970
+ artifactsSidebarVisible: this.#isArtifactsSidebarOpen,
930
971
  };
931
972
  }
932
973
 
@@ -0,0 +1,57 @@
1
+ // Copyright 2025 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as UI from '../../../ui/legacy/legacy.js';
6
+ import * as Lit from '../../../ui/lit/lit.js';
7
+
8
+ import artifactsViewerStyles from './artifactsViewer.css.js';
9
+
10
+ const {html, render} = Lit;
11
+
12
+ export interface ViewInput {
13
+ artifacts: [];
14
+ }
15
+
16
+ export const DEFAULT_VIEW = (
17
+ _input: ViewInput,
18
+ _output: Record<string, unknown>,
19
+ target: HTMLElement,
20
+ ): void => {
21
+ // clang-format off
22
+ render(
23
+ html`
24
+ <style>${artifactsViewerStyles}</style>
25
+ <div>
26
+ Artifacts Viewer
27
+ </div>
28
+ `,
29
+ target
30
+ );
31
+ // clang-format on
32
+ };
33
+
34
+ export type View = typeof DEFAULT_VIEW;
35
+
36
+ export class ArtifactsViewer extends UI.Widget.Widget {
37
+ #view: View;
38
+ constructor(element?: HTMLElement, view = DEFAULT_VIEW) {
39
+ super(element);
40
+ this.#view = view;
41
+ }
42
+
43
+ override wasShown(): void {
44
+ super.wasShown();
45
+ void this.requestUpdate();
46
+ }
47
+
48
+ override performUpdate(): Promise<void>|void {
49
+ this.#view(
50
+ {
51
+ artifacts: [],
52
+ },
53
+ {},
54
+ this.contentElement,
55
+ );
56
+ }
57
+ }
@@ -265,6 +265,7 @@ export interface Props {
265
265
  inputPlaceholder: Platform.UIString.LocalizedString;
266
266
  disclaimerText: Platform.UIString.LocalizedString;
267
267
  isTextInputEmpty: boolean;
268
+ isArtifactsSidebarOpen: boolean;
268
269
  uploadImageInputEnabled?: boolean;
269
270
  markdownRenderer: MarkdownLitRenderer;
270
271
  additionalFloatyContext: UI.Floaty.FloatyContextSelection[];
@@ -0,0 +1,10 @@
1
+ /*
2
+ * Copyright 2025 The Chromium Authors
3
+ * Use of this source code is governed by a BSD-style license that can be
4
+ * found in the LICENSE file.
5
+ */
6
+
7
+ :host {
8
+ display: flex;
9
+ height: 100%;
10
+ }
@@ -5,9 +5,7 @@
5
5
  import * as Host from '../../core/host/host.js';
6
6
  import * as i18n from '../../core/i18n/i18n.js';
7
7
  import type * as Platform from '../../core/platform/platform.js';
8
- import * as LegacyWrapper from '../../ui/components/legacy_wrapper/legacy_wrapper.js';
9
8
  import {createIcon} from '../../ui/kit/kit.js';
10
- import * as UI from '../../ui/legacy/legacy.js';
11
9
 
12
10
  import {ApplicationPanelTreeElement} from './ApplicationPanelTreeElement.js';
13
11
  import * as ApplicationComponents from './components/components.js';
@@ -23,8 +21,7 @@ const str_ = i18n.i18n.registerUIStrings('panels/application/BounceTrackingMitig
23
21
  export const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
24
22
 
25
23
  export class BounceTrackingMitigationsTreeElement extends ApplicationPanelTreeElement {
26
- private view?: LegacyWrapper.LegacyWrapper.LegacyWrapper<
27
- UI.Widget.Widget, ApplicationComponents.BounceTrackingMitigationsView.BounceTrackingMitigationsView>;
24
+ private view?: ApplicationComponents.BounceTrackingMitigationsView.BounceTrackingMitigationsView;
28
25
 
29
26
  constructor(resourcesPanel: ResourcesPanel) {
30
27
  super(resourcesPanel, i18nString(UIStrings.bounceTrackingMitigations), false, 'bounce-tracking-mitigations');
@@ -39,8 +36,7 @@ export class BounceTrackingMitigationsTreeElement extends ApplicationPanelTreeEl
39
36
  override onselect(selectedByUser?: boolean): boolean {
40
37
  super.onselect(selectedByUser);
41
38
  if (!this.view) {
42
- this.view = LegacyWrapper.LegacyWrapper.legacyWrapper(
43
- UI.Widget.Widget, new ApplicationComponents.BounceTrackingMitigationsView.BounceTrackingMitigationsView());
39
+ this.view = new ApplicationComponents.BounceTrackingMitigationsView.BounceTrackingMitigationsView();
44
40
  }
45
41
  this.showView(this.view);
46
42
  Host.userMetrics.panelShown('bounce-tracking-mitigations');
@@ -1,7 +1,6 @@
1
1
  // Copyright 2023 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-lit-render-outside-of-view */
5
4
 
6
5
  import '../../../ui/components/report_view/report_view.js';
7
6
  import '../../../ui/legacy/components/data_grid/data_grid.js';
@@ -9,7 +8,7 @@ import '../../../ui/legacy/components/data_grid/data_grid.js';
9
8
  import * as i18n from '../../../core/i18n/i18n.js';
10
9
  import * as SDK from '../../../core/sdk/sdk.js';
11
10
  import * as Buttons from '../../../ui/components/buttons/buttons.js';
12
- import * as LegacyWrapper from '../../../ui/components/legacy_wrapper/legacy_wrapper.js';
11
+ import * as UI from '../../../ui/legacy/legacy.js';
13
12
  import * as Lit from '../../../ui/lit/lit.js';
14
13
  import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
15
14
 
@@ -58,7 +57,8 @@ const UIStrings = {
58
57
  const str_ = i18n.i18n.registerUIStrings('panels/application/components/BounceTrackingMitigationsView.ts', UIStrings);
59
58
  export const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
60
59
 
61
- const enum ScreenStatusType {
60
+ export const enum ScreenStatusType {
61
+ INITIALIZING = 'Initializing',
62
62
  RUNNING = 'Running',
63
63
  RESULT = 'Result',
64
64
  DISABLED = 'Disabled',
@@ -68,119 +68,155 @@ export interface BounceTrackingMitigationsViewData {
68
68
  trackingSites: string[];
69
69
  }
70
70
 
71
- export class BounceTrackingMitigationsView extends LegacyWrapper.LegacyWrapper.WrappableComponent {
72
- readonly #shadow = this.attachShadow({mode: 'open'});
73
- #trackingSites: string[] = [];
74
- #screenStatus = ScreenStatusType.RESULT;
75
- #checkedFeature = false;
76
- #seenButtonClick = false;
77
-
78
- connectedCallback(): void {
79
- void this.#render();
80
- this.parentElement?.classList.add('overflow-auto');
81
- }
71
+ export interface ViewInput {
72
+ screenStatus: ScreenStatusType;
73
+ trackingSites: string[];
74
+ seenButtonClick: boolean;
75
+ runMitigations: () => Promise<void>;
76
+ }
82
77
 
83
- async #render(): Promise<void> {
84
- // clang-format off
85
- Lit.render(html`
86
- <style>${bounceTrackingMitigationsViewStyles}</style>
87
- <devtools-report .data=${{reportTitle: i18nString(UIStrings.bounceTrackingMitigationsTitle)}}
88
- jslog=${VisualLogging.pane('bounce-tracking-mitigations')}>
89
- ${await this.#renderMainFrameInformation()}
90
- </devtools-report>
91
- `, this.#shadow, {host: this});
92
- // clang-format on
78
+ const renderForceRunButton = (input: ViewInput): Lit.TemplateResult => {
79
+ const isMitigationRunning = (input.screenStatus === ScreenStatusType.RUNNING);
80
+
81
+ // clang-format off
82
+ return html`
83
+ <devtools-button
84
+ aria-label=${i18nString(UIStrings.forceRun)}
85
+ .disabled=${isMitigationRunning}
86
+ .spinner=${isMitigationRunning}
87
+ .variant=${Buttons.Button.Variant.PRIMARY}
88
+ @click=${input.runMitigations}
89
+ jslog=${VisualLogging.action('force-run').track({click: true})}>
90
+ ${isMitigationRunning ? html`
91
+ ${i18nString(UIStrings.runningMitigations)}`:`
92
+ ${i18nString(UIStrings.forceRun)}
93
+ `}
94
+ </devtools-button>
95
+ `;
96
+ // clang-format on
97
+ };
98
+
99
+ const renderDeletedSitesOrNoSitesMessage = (input: ViewInput): Lit.LitTemplate => {
100
+ if (!input.seenButtonClick) {
101
+ return Lit.nothing;
93
102
  }
94
103
 
95
- async #renderMainFrameInformation(): Promise<Lit.TemplateResult> {
96
- if (!this.#checkedFeature) {
97
- await this.#checkFeatureState();
98
- }
99
-
100
- if (this.#screenStatus === ScreenStatusType.DISABLED) {
101
- // clang-format off
102
- return html`
103
- <devtools-report-section>
104
- ${i18nString(UIStrings.featureDisabled)}
105
- </devtools-report-section>
106
- `;
107
- // clang-format on
108
- }
109
-
104
+ if (input.trackingSites.length === 0) {
110
105
  // clang-format off
111
106
  return html`
112
107
  <devtools-report-section>
113
- ${this.#renderForceRunButton()}
114
- </devtools-report-section>
115
- ${this.#renderDeletedSitesOrNoSitesMessage()}
116
- <devtools-report-divider>
117
- </devtools-report-divider>
118
- <devtools-report-section>
119
- <x-link href="https://privacycg.github.io/nav-tracking-mitigations/#bounce-tracking-mitigations" class="link"
120
- jslog=${VisualLogging.link('learn-more').track({click: true})}>
121
- ${i18nString(UIStrings.learnMore)}
122
- </x-link>
108
+ ${(input.screenStatus === ScreenStatusType.RUNNING) ? html`
109
+ ${i18nString(UIStrings.checkingPotentialTrackers)}`:`
110
+ ${i18nString(UIStrings.noPotentialBounceTrackersIdentified)}
111
+ `}
123
112
  </devtools-report-section>
124
113
  `;
125
114
  // clang-format on
126
115
  }
127
116
 
128
- #renderForceRunButton(): Lit.TemplateResult {
129
- const isMitigationRunning = (this.#screenStatus === ScreenStatusType.RUNNING);
117
+ // clang-format off
118
+ return html`
119
+ <devtools-report-section>
120
+ <devtools-data-grid striped inline>
121
+ <table>
122
+ <tr>
123
+ <th id="sites" weight="10" sortable>
124
+ ${i18nString(UIStrings.stateDeletedFor)}
125
+ </th>
126
+ </tr>
127
+ ${input.trackingSites.map(site => html`
128
+ <tr><td>${site}</td></tr>`)}
129
+ </table>
130
+ </devtools-data-grid>
131
+ </devtools-report-section>
132
+ `;
133
+ // clang-format on
134
+ };
135
+
136
+ const renderMainFrameInformation = (input: ViewInput): Lit.LitTemplate => {
137
+ if (input.screenStatus === ScreenStatusType.INITIALIZING) {
138
+ return Lit.nothing;
139
+ }
130
140
 
141
+ if (input.screenStatus === ScreenStatusType.DISABLED) {
131
142
  // clang-format off
132
143
  return html`
133
- <devtools-button
134
- aria-label=${i18nString(UIStrings.forceRun)}
135
- .disabled=${isMitigationRunning}
136
- .spinner=${isMitigationRunning}
137
- .variant=${Buttons.Button.Variant.PRIMARY}
138
- @click=${this.#runMitigations}
139
- jslog=${VisualLogging.action('force-run').track({click: true})}>
140
- ${isMitigationRunning ? html`
141
- ${i18nString(UIStrings.runningMitigations)}`:`
142
- ${i18nString(UIStrings.forceRun)}
143
- `}
144
- </devtools-button>
144
+ <devtools-report-section>
145
+ ${i18nString(UIStrings.featureDisabled)}
146
+ </devtools-report-section>
145
147
  `;
146
148
  // clang-format on
147
149
  }
148
150
 
149
- #renderDeletedSitesOrNoSitesMessage(): Lit.LitTemplate {
150
- if (!this.#seenButtonClick) {
151
- return Lit.nothing;
152
- }
151
+ // clang-format off
152
+ return html`
153
+ <devtools-report-section>
154
+ ${renderForceRunButton(input)}
155
+ </devtools-report-section>
156
+ ${renderDeletedSitesOrNoSitesMessage(input)}
157
+ <devtools-report-divider>
158
+ </devtools-report-divider>
159
+ <devtools-report-section>
160
+ <x-link href="https://privacycg.github.io/nav-tracking-mitigations/#bounce-tracking-mitigations" class="link"
161
+ jslog=${VisualLogging.link('learn-more').track({click: true})}>
162
+ ${i18nString(UIStrings.learnMore)}
163
+ </x-link>
164
+ </devtools-report-section>
165
+ `;
166
+ // clang-format on
167
+ };
168
+
169
+ export const DEFAULT_VIEW = (input: ViewInput, _output: undefined, target: HTMLElement): void => {
170
+ // clang-format off
171
+ Lit.render(html`
172
+ <style>${bounceTrackingMitigationsViewStyles}</style>
173
+ <style>${UI.inspectorCommonStyles}</style>
174
+ <devtools-report .data=${{reportTitle: i18nString(UIStrings.bounceTrackingMitigationsTitle)}}
175
+ jslog=${VisualLogging.pane('bounce-tracking-mitigations')}>
176
+ ${renderMainFrameInformation(input)}
177
+ </devtools-report>
178
+ `, target);
179
+ // clang-format on
180
+ };
181
+
182
+ type ViewFunction = typeof DEFAULT_VIEW;
183
+
184
+ export class BounceTrackingMitigationsView extends UI.Widget.Widget {
185
+ #trackingSites: string[] = [];
186
+ #screenStatus = ScreenStatusType.INITIALIZING;
187
+ #seenButtonClick = false;
188
+ #view: ViewFunction;
189
+
190
+ constructor(element?: HTMLElement, view: ViewFunction = DEFAULT_VIEW) {
191
+ super(element, {useShadowDom: true, classes: ['overflow-auto']});
153
192
 
154
- if (this.#trackingSites.length === 0) {
155
- // clang-format off
156
- return html`
157
- <devtools-report-section>
158
- ${(this.#screenStatus === ScreenStatusType.RUNNING) ? html`
159
- ${i18nString(UIStrings.checkingPotentialTrackers)}`:`
160
- ${i18nString(UIStrings.noPotentialBounceTrackersIdentified)}
161
- `}
162
- </devtools-report-section>
163
- `;
164
- // clang-format on
193
+ this.#view = view;
194
+
195
+ const mainTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
196
+ if (!mainTarget) {
197
+ this.#screenStatus = ScreenStatusType.RESULT;
198
+ } else {
199
+ void mainTarget.systemInfo().invoke_getFeatureState({featureState: 'DIPS'}).then(state => {
200
+ this.#screenStatus = state.featureEnabled ? ScreenStatusType.RESULT : ScreenStatusType.DISABLED;
201
+ this.requestUpdate();
202
+ });
165
203
  }
204
+ }
166
205
 
167
- // clang-format off
168
- return html`
169
- <devtools-report-section>
170
- <devtools-data-grid striped inline>
171
- <table>
172
- <tr>
173
- <th id="sites" weight="10" sortable>
174
- ${i18nString(UIStrings.stateDeletedFor)}
175
- </th>
176
- </tr>
177
- ${this.#trackingSites.map(site => html`
178
- <tr><td>${site}</td></tr>`)}
179
- </table>
180
- </devtools-data-grid>
181
- </devtools-report-section>
182
- `;
183
- // clang-format on
206
+ override wasShown(): void {
207
+ super.wasShown();
208
+ this.requestUpdate();
209
+ }
210
+
211
+ override performUpdate(): void {
212
+ this.#view(
213
+ {
214
+ screenStatus: this.#screenStatus,
215
+ trackingSites: this.#trackingSites,
216
+ seenButtonClick: this.#seenButtonClick,
217
+ runMitigations: this.#runMitigations.bind(this),
218
+ },
219
+ undefined, this.contentElement);
184
220
  }
185
221
 
186
222
  async #runMitigations(): Promise<void> {
@@ -192,7 +228,7 @@ export class BounceTrackingMitigationsView extends LegacyWrapper.LegacyWrapper.W
192
228
  this.#seenButtonClick = true;
193
229
  this.#screenStatus = ScreenStatusType.RUNNING;
194
230
 
195
- void this.#render();
231
+ this.requestUpdate();
196
232
 
197
233
  const response = await mainTarget.storageAgent().invoke_runBounceTrackingMitigations();
198
234
  this.#trackingSites = [];
@@ -205,27 +241,6 @@ export class BounceTrackingMitigationsView extends LegacyWrapper.LegacyWrapper.W
205
241
 
206
242
  #renderMitigationsResult(): void {
207
243
  this.#screenStatus = ScreenStatusType.RESULT;
208
- void this.#render();
209
- }
210
-
211
- async #checkFeatureState(): Promise<void> {
212
- this.#checkedFeature = true;
213
-
214
- const mainTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
215
- if (!mainTarget) {
216
- return;
217
- }
218
-
219
- if (!(await mainTarget.systemInfo().invoke_getFeatureState({featureState: 'DIPS'})).featureEnabled) {
220
- this.#screenStatus = ScreenStatusType.DISABLED;
221
- }
222
- }
223
- }
224
-
225
- customElements.define('devtools-bounce-tracking-mitigations-view', BounceTrackingMitigationsView);
226
-
227
- declare global {
228
- interface HTMLElementTagNameMap {
229
- 'devtools-bounce-tracking-mitigations-view': BounceTrackingMitigationsView;
244
+ this.requestUpdate();
230
245
  }
231
246
  }
@@ -417,7 +417,13 @@ export class PreloadingAttemptView extends UI.Widget.VBox {
417
417
  this.ruleSetSelector = new PreloadingRuleSetSelector(() => this.render());
418
418
  toolbar.appendToolbarItem(this.ruleSetSelector.item());
419
419
 
420
- this.preloadingGrid.addEventListener('select', this.onPreloadingGridCellFocused.bind(this));
420
+ this.preloadingGrid.onSelect = this.onPreloadingGridCellFocused.bind(this);
421
+
422
+ const preloadingGridContainer = document.createElement('div');
423
+ preloadingGridContainer.className = 'preloading-grid-widget-container';
424
+ preloadingGridContainer.style = 'height: 100%';
425
+ this.preloadingGrid.show(preloadingGridContainer, null, true);
426
+
421
427
  render(
422
428
  html`
423
429
  <div class="empty-state">
@@ -433,7 +439,7 @@ export class PreloadingAttemptView extends UI.Widget.VBox {
433
439
  </div>
434
440
  <devtools-split-view sidebar-position="second">
435
441
  <div slot="main" class="overflow-auto" style="height: 100%">
436
- ${this.preloadingGrid}
442
+ ${preloadingGridContainer}
437
443
  </div>
438
444
  <div slot="sidebar" class="overflow-auto" style="height: 100%">
439
445
  ${this.preloadingDetails}
@@ -500,15 +506,15 @@ export class PreloadingAttemptView extends UI.Widget.VBox {
500
506
  ruleSets,
501
507
  };
502
508
  });
503
- this.preloadingGrid.update({rows, pageURL: pageURL()});
509
+ this.preloadingGrid.rows = rows;
510
+ this.preloadingGrid.pageURL = pageURL();
504
511
  this.contentElement.classList.toggle('empty', rows.length === 0);
505
512
 
506
513
  this.updatePreloadingDetails();
507
514
  }
508
515
 
509
- private onPreloadingGridCellFocused(event: Event): void {
510
- const focusedEvent = event as CustomEvent<SDK.PreloadingModel.PreloadingAttemptId>;
511
- this.focusedPreloadingAttemptId = focusedEvent.detail;
516
+ private onPreloadingGridCellFocused({rowId}: {rowId: string}): void {
517
+ this.focusedPreloadingAttemptId = rowId;
512
518
  this.render();
513
519
  }
514
520