chrome-devtools-frontend 1.0.1593959 → 1.0.1595925

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 (79) hide show
  1. package/docs/contributing/settings-experiments-features.md +25 -5
  2. package/front_end/core/common/README.md +126 -0
  3. package/front_end/core/sdk/CSSModel.ts +22 -0
  4. package/front_end/core/sdk/CSSNavigation.ts +33 -0
  5. package/front_end/core/sdk/CSSRule.ts +12 -2
  6. package/front_end/core/sdk/sdk-meta.ts +22 -18
  7. package/front_end/core/sdk/sdk.ts +2 -0
  8. package/front_end/design_system_tokens.css +538 -259
  9. package/front_end/entrypoints/main/main-meta.ts +2 -2
  10. package/front_end/generated/InspectorBackendCommands.ts +3 -5
  11. package/front_end/generated/SupportedCSSProperties.js +12 -0
  12. package/front_end/generated/protocol-mapping.d.ts +0 -8
  13. package/front_end/generated/protocol-proxy-api.d.ts +0 -6
  14. package/front_end/generated/protocol.ts +1 -19
  15. package/front_end/models/issues_manager/Issue.ts +1 -0
  16. package/front_end/models/issues_manager/IssueAggregator.ts +9 -0
  17. package/front_end/models/issues_manager/IssuesManager.ts +5 -0
  18. package/front_end/models/issues_manager/SelectivePermissionsInterventionIssue.ts +65 -0
  19. package/front_end/models/issues_manager/descriptions/selectivePermissionsIntervention.md +7 -0
  20. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  21. package/front_end/models/javascript_metadata/NativeFunctions.js +49 -10
  22. package/front_end/models/persistence/persistence-meta.ts +4 -4
  23. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +12 -1
  24. package/front_end/panels/ai_assistance/components/ChatInput.ts +37 -30
  25. package/front_end/panels/ai_assistance/components/ChatView.ts +4 -2
  26. package/front_end/panels/ai_assistance/components/WalkthroughView.ts +10 -7
  27. package/front_end/panels/ai_assistance/components/chatInput.css +26 -1
  28. package/front_end/panels/ai_assistance/components/walkthroughView.css +15 -0
  29. package/front_end/panels/application/StorageView.ts +8 -2
  30. package/front_end/panels/application/preloading/PreloadingView.ts +105 -7
  31. package/front_end/panels/application/preloading/preloadingView.css +4 -0
  32. package/front_end/panels/console/ConsoleView.ts +2 -2
  33. package/front_end/panels/console/console-meta.ts +18 -14
  34. package/front_end/panels/elements/ComputedStyleWidget.ts +12 -7
  35. package/front_end/panels/elements/ElementsPanel.ts +1 -1
  36. package/front_end/panels/elements/ElementsTreeOutline.ts +4 -6
  37. package/front_end/panels/elements/ImagePreviewPopover.ts +6 -9
  38. package/front_end/panels/elements/StylePropertiesSection.ts +30 -0
  39. package/front_end/panels/elements/StylesSidebarPane.ts +13 -7
  40. package/front_end/panels/elements/elements-meta.ts +12 -8
  41. package/front_end/panels/issues/AffectedSelectivePermissionsInterventionView.ts +120 -0
  42. package/front_end/panels/issues/IssueView.ts +2 -0
  43. package/front_end/panels/issues/IssuesPane.ts +6 -0
  44. package/front_end/panels/issues/issues.ts +2 -0
  45. package/front_end/panels/lighthouse/LighthouseStartView.ts +99 -38
  46. package/front_end/panels/lighthouse/LighthouseTimespanView.ts +53 -14
  47. package/front_end/panels/lighthouse/RadioSetting.ts +33 -19
  48. package/front_end/panels/lighthouse/lighthouse.ts +2 -0
  49. package/front_end/panels/media/PlayerMessagesView.ts +62 -49
  50. package/front_end/panels/media/media.ts +2 -0
  51. package/front_end/panels/media/playerMessagesView.css +1 -1
  52. package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +49 -24
  53. package/front_end/panels/settings/FrameworkIgnoreListSettingsTab.ts +2 -2
  54. package/front_end/panels/sources/sources-meta.ts +8 -4
  55. package/front_end/panels/utils/utils.ts +11 -5
  56. package/front_end/third_party/chromium/README.chromium +1 -1
  57. package/front_end/third_party/lit/lib/async-directive.d.ts +465 -0
  58. package/front_end/third_party/lit/lib/async-directive.js +23 -0
  59. package/front_end/third_party/lit/lib/async-directive.js.map +1 -0
  60. package/front_end/third_party/lit/lib/decorators.d.ts +7 -1
  61. package/front_end/third_party/lit/lib/decorators.js +2 -2
  62. package/front_end/third_party/lit/lib/decorators.js.map +1 -1
  63. package/front_end/third_party/lit/lib/directive.js.map +1 -1
  64. package/front_end/third_party/lit/lib/directives.d.ts +27 -8
  65. package/front_end/third_party/lit/lib/directives.js +8 -8
  66. package/front_end/third_party/lit/lib/directives.js.map +1 -1
  67. package/front_end/third_party/lit/lib/lit.d.ts +15 -5
  68. package/front_end/third_party/lit/lib/lit.js +4 -4
  69. package/front_end/third_party/lit/lib/lit.js.map +1 -1
  70. package/front_end/third_party/lit/lib/static-html.js +2 -2
  71. package/front_end/third_party/lit/lib/static-html.js.map +1 -1
  72. package/front_end/third_party/lit/lit.ts +2 -1
  73. package/front_end/third_party/lit/rollup.config.mjs +1 -1
  74. package/front_end/ui/components/markdown_view/MarkdownLinksMap.ts +5 -0
  75. package/front_end/ui/legacy/components/utils/ImagePreview.ts +27 -23
  76. package/front_end/ui/lit/lit.ts +1 -0
  77. package/front_end/ui/visual_logging/Debugging.ts +1 -1
  78. package/front_end/ui/visual_logging/KnownContextValues.ts +4 -0
  79. package/package.json +1 -1
@@ -43,6 +43,7 @@ import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
43
43
  import * as CodeHighlighter from '../../ui/components/code_highlighter/code_highlighter.js';
44
44
  import * as Highlighting from '../../ui/components/highlighting/highlighting.js';
45
45
  import * as IssueCounter from '../../ui/components/issue_counter/issue_counter.js';
46
+ import * as UIComponentUtils from '../../ui/legacy/components/utils/utils.js';
46
47
  import * as UI from '../../ui/legacy/legacy.js';
47
48
  import {html, nothing, render} from '../../ui/lit/lit.js';
48
49
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
@@ -555,18 +556,15 @@ export class ElementsTreeOutline extends
555
556
  }
556
557
  return link;
557
558
  },
558
- link => {
559
+ async link => {
559
560
  const listItem = UI.UIUtils.enclosingNodeOrSelfWithNodeName(link, 'li');
560
561
  if (!listItem) {
561
- return null;
562
+ return undefined;
562
563
  }
563
564
 
564
565
  const treeElement =
565
566
  (UI.TreeOutline.TreeElement.getTreeElementBylistItemNode(listItem) as ElementsTreeElement | undefined);
566
- if (!treeElement) {
567
- return null;
568
- }
569
- return treeElement.node();
567
+ return await UIComponentUtils.ImagePreview.loadPrecomputedFeatures(treeElement?.node());
570
568
  });
571
569
 
572
570
  this.updateRecords = new Map();
@@ -3,7 +3,6 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import type * as Platform from '../../core/platform/platform.js';
6
- import type * as SDK from '../../core/sdk/sdk.js';
7
6
  import * as Components from '../../ui/legacy/components/utils/utils.js';
8
7
  import * as UI from '../../ui/legacy/legacy.js';
9
8
 
@@ -15,13 +14,15 @@ import * as UI from '../../ui/legacy/legacy.js';
15
14
  */
16
15
  export class ImagePreviewPopover {
17
16
  private readonly getLinkElement: (arg0: Event) => Element | null;
18
- private readonly getDOMNode: (arg0: Element) => SDK.DOMModel.DOMNode | null;
19
17
  private readonly popover: UI.PopoverHelper.PopoverHelper;
18
+
19
+ #getNodeFeatures: (link: Element) => Promise<Components.ImagePreview.PrecomputedFeatures|undefined>;
20
+
20
21
  constructor(
21
22
  container: HTMLElement, getLinkElement: (arg0: Event) => Element | null,
22
- getDOMNode: (arg0: Element) => SDK.DOMModel.DOMNode | null) {
23
+ getNodeFeatures: (arg0: Element) => Promise<Components.ImagePreview.PrecomputedFeatures|undefined>) {
23
24
  this.getLinkElement = getLinkElement;
24
- this.getDOMNode = getDOMNode;
25
+ this.#getNodeFeatures = getNodeFeatures;
25
26
  this.popover =
26
27
  new UI.PopoverHelper.PopoverHelper(container, this.handleRequest.bind(this), 'elements.image-preview');
27
28
  this.popover.setTimeout(0, 100);
@@ -39,11 +40,7 @@ export class ImagePreviewPopover {
39
40
  return {
40
41
  box: link.boxInWindow(),
41
42
  show: async (popover: UI.GlassPane.GlassPane) => {
42
- const node = this.getDOMNode((link));
43
- if (!node) {
44
- return false;
45
- }
46
- const precomputedFeatures = await Components.ImagePreview.ImagePreview.loadDimensionsForNode(node);
43
+ const precomputedFeatures = await this.#getNodeFeatures(link);
47
44
  const preview = await Components.ImagePreview.ImagePreview.build(href, true, {
48
45
  precomputedFeatures,
49
46
  align: Components.ImagePreview.Align.CENTER,
@@ -807,6 +807,7 @@ export class StylePropertiesSection {
807
807
  let scopeIndex = 0;
808
808
  let supportsIndex = 0;
809
809
  let nestingIndex = 0;
810
+ let navigationsIndex = 0;
810
811
  this.nestingLevel = 0;
811
812
  for (const ruleType of rule.ruleTypes) {
812
813
  let ancestorRuleElement;
@@ -829,6 +830,9 @@ export class StylePropertiesSection {
829
830
  case Protocol.CSS.CSSRuleType.StartingStyleRule:
830
831
  ancestorRuleElement = this.createStartingStyleElement();
831
832
  break;
833
+ case Protocol.CSS.CSSRuleType.NavigationRule:
834
+ ancestorRuleElement = this.createNavigationElement(rule.navigations[navigationsIndex++]);
835
+ break;
832
836
  }
833
837
  if (ancestorRuleElement) {
834
838
  this.#ancestorRuleListElement.prepend(ancestorRuleElement);
@@ -1007,6 +1011,27 @@ export class StylePropertiesSection {
1007
1011
  return supportsElement;
1008
1012
  }
1009
1013
 
1014
+ protected createNavigationElement(navigation: SDK.CSSNavigation.CSSNavigation): ElementsComponents.CSSQuery.CSSQuery
1015
+ |undefined {
1016
+ if (!navigation.text) {
1017
+ return;
1018
+ }
1019
+
1020
+ let onQueryTextClick;
1021
+ if (navigation.styleSheetId) {
1022
+ onQueryTextClick = this.handleQueryRuleClick.bind(this, navigation);
1023
+ }
1024
+
1025
+ const navigationElement = new ElementsComponents.CSSQuery.CSSQuery();
1026
+ navigationElement.data = {
1027
+ queryPrefix: '@navigation',
1028
+ queryText: navigation.text,
1029
+ onQueryTextClick,
1030
+ jslogContext: 'navigation',
1031
+ };
1032
+ return navigationElement;
1033
+ }
1034
+
1010
1035
  protected createNestingElement(nestingSelector?: string): HTMLElement|undefined {
1011
1036
  if (!nestingSelector) {
1012
1037
  return;
@@ -1419,6 +1444,8 @@ export class StylePropertiesSection {
1419
1444
  success = await cssModel.setSupportsText(query.styleSheetId, range, newContent);
1420
1445
  } else if (query instanceof SDK.CSSScope.CSSScope) {
1421
1446
  success = await cssModel.setScopeText(query.styleSheetId, range, newContent);
1447
+ } else if (query instanceof SDK.CSSNavigation.CSSNavigation) {
1448
+ success = await cssModel.setNavigationText(query.styleSheetId, range, newContent);
1422
1449
  } else {
1423
1450
  success = await cssModel.setMediaText(query.styleSheetId, range, newContent);
1424
1451
  }
@@ -1871,6 +1898,9 @@ export class FunctionRuleSection extends StylePropertiesSection {
1871
1898
  if ('supports' in condition) {
1872
1899
  return this.createSupportsElement(condition.supports);
1873
1900
  }
1901
+ if ('navigation' in condition) {
1902
+ return this.createNavigationElement(condition.navigation);
1903
+ }
1874
1904
  return;
1875
1905
  }
1876
1906
 
@@ -236,13 +236,19 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
236
236
  this.contentElement.addEventListener('copy', this.clipboardCopy.bind(this));
237
237
 
238
238
  this.boundOnScroll = this.onScroll.bind(this);
239
- this.imagePreviewPopover = new ImagePreviewPopover(this.contentElement, event => {
240
- const link = event.composedPath()[0];
241
- if (link instanceof Element) {
242
- return link;
243
- }
244
- return null;
245
- }, () => this.node());
239
+ this.imagePreviewPopover = new ImagePreviewPopover(
240
+ this.contentElement,
241
+ event => {
242
+ const link = event.composedPath()[0];
243
+ if (link instanceof Element) {
244
+ return link;
245
+ }
246
+ return null;
247
+ },
248
+ async () => {
249
+ const features = await Components.ImagePreview.loadPrecomputedFeatures(this.node());
250
+ return features;
251
+ });
246
252
 
247
253
  UI.ViewManager.ViewManager.instance().addEventListener(UI.ViewManager.Events.VIEW_VISIBILITY_CHANGED, event => {
248
254
  if (event.data.revealedViewId === 'animations' || event.data.hiddenViewId === 'animations') {
@@ -110,9 +110,13 @@ const UIStrings = {
110
110
  /**
111
111
  * @description Title of a setting under the Elements category. Whether to show/hide code comments in HTML.
112
112
  */
113
+ htmlComments: 'HTML comments',
114
+ /**
115
+ * @description Title of an option under the Elements category that can be invoked through the Command Menu
116
+ */
113
117
  showHtmlComments: 'Show `HTML` comments',
114
118
  /**
115
- * @description Title of a setting under the Elements category. Whether to show/hide code comments in HTML.
119
+ * @description Title of an option under the Elements category that can be invoked through the Command Menu
116
120
  */
117
121
  hideHtmlComments: 'Hide `HTML` comments',
118
122
  /**
@@ -126,12 +130,12 @@ const UIStrings = {
126
130
  * the inspect tooltip (an information pane that hovers next to selected DOM elements) has extra
127
131
  * detail.
128
132
  */
129
- showDetailedInspectTooltip: 'Show detailed inspect tooltip',
133
+ detailedInspectTooltip: 'Detailed inspect tooltip',
130
134
  /**
131
135
  * @description Title of a setting under the Elements category in Settings. Turns on a mode where
132
136
  * hovering over CSS properties in the Styles pane will display a popover with documentation.
133
137
  */
134
- showCSSDocumentationTooltip: 'Show CSS documentation tooltip',
138
+ CSSDocumentationTooltip: 'CSS documentation tooltip',
135
139
  /**
136
140
  * @description A context menu item (command) in the Elements panel that copy the styles of
137
141
  * the HTML element.
@@ -146,7 +150,7 @@ const UIStrings = {
146
150
  * @description Title of a setting under the Elements category. Whether to show/hide hide
147
151
  * the shadow DOM nodes of HTML elements that are built into the browser (e.g. the <input> element).
148
152
  */
149
- showUserAgentShadowDOM: 'Show user agent shadow `DOM`',
153
+ userAgentShadowDOM: 'User agent shadow `DOM`',
150
154
  /**
151
155
  * @description Command for showing the 'Computed' tool. Displays computed CSS styles in Elements sidebar.
152
156
  */
@@ -489,7 +493,7 @@ Common.Settings.registerSettingExtension({
489
493
  category: Common.Settings.SettingCategory.ELEMENTS,
490
494
  storageType: Common.Settings.SettingStorageType.SYNCED,
491
495
  order: 1,
492
- title: i18nLazyString(UIStrings.showUserAgentShadowDOM),
496
+ title: i18nLazyString(UIStrings.userAgentShadowDOM),
493
497
  settingName: 'show-ua-shadow-dom',
494
498
  settingType: Common.Settings.SettingType.BOOLEAN,
495
499
  defaultValue: false,
@@ -528,7 +532,7 @@ Common.Settings.registerSettingExtension({
528
532
  category: Common.Settings.SettingCategory.ELEMENTS,
529
533
  storageType: Common.Settings.SettingStorageType.SYNCED,
530
534
  order: 3,
531
- title: i18nLazyString(UIStrings.showHtmlComments),
535
+ title: i18nLazyString(UIStrings.htmlComments),
532
536
  settingName: 'show-html-comments',
533
537
  settingType: Common.Settings.SettingType.BOOLEAN,
534
538
  defaultValue: true,
@@ -558,7 +562,7 @@ Common.Settings.registerSettingExtension({
558
562
  category: Common.Settings.SettingCategory.ELEMENTS,
559
563
  storageType: Common.Settings.SettingStorageType.SYNCED,
560
564
  order: 5,
561
- title: i18nLazyString(UIStrings.showDetailedInspectTooltip),
565
+ title: i18nLazyString(UIStrings.detailedInspectTooltip),
562
566
  settingName: 'show-detailed-inspect-tooltip',
563
567
  settingType: Common.Settings.SettingType.BOOLEAN,
564
568
  defaultValue: true,
@@ -581,7 +585,7 @@ Common.Settings.registerSettingExtension({
581
585
  Common.Settings.registerSettingExtension({
582
586
  category: Common.Settings.SettingCategory.ELEMENTS,
583
587
  storageType: Common.Settings.SettingStorageType.SYNCED,
584
- title: i18nLazyString(UIStrings.showCSSDocumentationTooltip),
588
+ title: i18nLazyString(UIStrings.CSSDocumentationTooltip),
585
589
  settingName: 'show-css-property-documentation-on-hover',
586
590
  settingType: Common.Settings.SettingType.BOOLEAN,
587
591
  defaultValue: true,
@@ -0,0 +1,120 @@
1
+ // Copyright 2026 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 i18n from '../../core/i18n/i18n.js';
6
+ import type * as Platform from '../../core/platform/platform.js';
7
+ import type * as SDK from '../../core/sdk/sdk.js';
8
+ import type * as Protocol from '../../generated/protocol.js';
9
+ import * as Bindings from '../../models/bindings/bindings.js';
10
+ import type * as IssuesManager from '../../models/issues_manager/issues_manager.js';
11
+ import * as Components from '../../ui/legacy/components/utils/utils.js';
12
+ import * as UI from '../../ui/legacy/legacy.js';
13
+ import {Directives, html, type LitTemplate, nothing, render} from '../../ui/lit/lit.js';
14
+
15
+ import {AffectedResourcesView} from './AffectedResourcesView.js';
16
+
17
+ const UIStrings = {
18
+ /**
19
+ * @description Label for number of affected resources indication in issue view
20
+ */
21
+ nViolations: '{n, plural, =1 {# violation} other {# violations}}',
22
+ /**
23
+ * @description Title for the API column in the Selective Permissions Intervention affected resources list
24
+ */
25
+ api: 'API',
26
+ /**
27
+ * @description Title for the Script column in the Selective Permissions Intervention affected resources list
28
+ */
29
+ script: 'Script',
30
+ /**
31
+ * @description Title for the Ad Ancestry column in the Selective Permissions Intervention affected resources list
32
+ */
33
+ adAncestry: 'Ad Ancestry',
34
+ /**
35
+ * @description Text for unknown value
36
+ */
37
+ unknown: 'unknown',
38
+ /**
39
+ * @description Text for loading state
40
+ */
41
+ loading: 'loading…',
42
+ } as const;
43
+ const str_ = i18n.i18n.registerUIStrings('panels/issues/AffectedSelectivePermissionsInterventionView.ts', UIStrings);
44
+ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
45
+
46
+ export class AffectedSelectivePermissionsInterventionView extends AffectedResourcesView {
47
+ readonly #linkifier = new Components.Linkifier.Linkifier();
48
+
49
+ protected getResourceNameWithCount(count: number): string {
50
+ return i18nString(UIStrings.nViolations, {n: count});
51
+ }
52
+
53
+ #render(): void {
54
+ const issues = Array.from(this.issue.getSelectivePermissionsInterventionIssues());
55
+ // eslint-disable-next-line @devtools/no-lit-render-outside-of-view
56
+ render(
57
+ html`
58
+ <tr>
59
+ <td class="affected-resource-header">${i18nString(UIStrings.api)}</td>
60
+ <td class="affected-resource-header">${i18nString(UIStrings.script)}</td>
61
+ <td class="affected-resource-header">${i18nString(UIStrings.adAncestry)}</td>
62
+ </tr>
63
+ ${issues.map(issue => this.#renderDetail(issue))}
64
+ `,
65
+ this.affectedResources, {host: this});
66
+ this.updateAffectedResourceCount(issues.length);
67
+ }
68
+
69
+ #renderDetail(issue: IssuesManager.SelectivePermissionsInterventionIssue.SelectivePermissionsInterventionIssue):
70
+ LitTemplate {
71
+ const details = issue.details();
72
+ const issuesModel = issue.model();
73
+
74
+ const stackTracePromise = (details.stackTrace && issuesModel) ?
75
+ this.#resolveStackTrace(details.stackTrace, issuesModel) :
76
+ Promise.resolve(html`<span>${i18nString(UIStrings.unknown)}</span>`);
77
+
78
+ const target = issuesModel ? issuesModel.target() : null;
79
+
80
+ return html`
81
+ <tr class="affected-resource-directive">
82
+ <td>${details.apiName}</td>
83
+ <td>${Directives.until(stackTracePromise, html`<span>${i18nString(UIStrings.loading)}</span>`)}</td>
84
+ <td class="affected-resource-cell">
85
+ <div class="ad-ancestry-list">
86
+ ${(details.adAncestry?.adAncestryChain || []).map(script => {
87
+ const link = this.#linkifier.linkifyScriptLocation(
88
+ target, script.scriptId, script.name as Platform.DevToolsPath.UrlString, 0);
89
+ return html`<div>${link}</div>`;
90
+ })}
91
+ ${
92
+ details.adAncestry?.rootScriptFilterlistRule ?
93
+ html`<div>Rule: ${details.adAncestry.rootScriptFilterlistRule}</div>` :
94
+ nothing}
95
+ </div>
96
+ </td>
97
+ </tr>
98
+ `;
99
+ }
100
+
101
+ async #resolveStackTrace(stackTrace: Protocol.Runtime.StackTrace, issuesModel: SDK.IssuesModel.IssuesModel):
102
+ Promise<LitTemplate> {
103
+ const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
104
+ const stackTraceTranslated =
105
+ await debuggerWorkspaceBinding.createStackTraceFromProtocolRuntime(stackTrace, issuesModel.target());
106
+ return html`
107
+ <devtools-widget .widgetConfig=${UI.Widget.widgetConfig(Components.JSPresentationUtils.StackTracePreviewContent, {
108
+ stackTrace: stackTraceTranslated,
109
+ options: {expandable: true},
110
+ })}>
111
+ </devtools-widget>
112
+ `;
113
+ }
114
+
115
+ update(): void {
116
+ this.requestResolver.clear();
117
+ this.#linkifier.reset();
118
+ this.#render();
119
+ }
120
+ }
@@ -29,6 +29,7 @@ import {AffectedMetadataAllowedSitesView} from './AffectedMetadataAllowedSitesVi
29
29
  import {AffectedPartitioningBlobURLView} from './AffectedPartitioningBlobURLView.js';
30
30
  import {AffectedPermissionElementsView} from './AffectedPermissionElementsView.js';
31
31
  import {AffectedItem, AffectedResourcesView, extractShortPath} from './AffectedResourcesView.js';
32
+ import {AffectedSelectivePermissionsInterventionView} from './AffectedSelectivePermissionsInterventionView.js';
32
33
  import {AffectedSharedArrayBufferIssueDetailsView} from './AffectedSharedArrayBufferIssueDetailsView.js';
33
34
  import {AffectedSourcesView} from './AffectedSourcesView.js';
34
35
  import {AffectedTrackingSitesView} from './AffectedTrackingSitesView.js';
@@ -260,6 +261,7 @@ export class IssueView extends UI.TreeOutline.TreeElement {
260
261
  new AffectedDescendantsWithinSelectElementView(this, this.#issue, 'disallowed-select-descendants-details'),
261
262
  new AffectedPartitioningBlobURLView(this, this.#issue, 'partitioning-blob-url-details'),
262
263
  new AffectedPermissionElementsView(this, this.#issue, 'permission-element-elements'),
264
+ new AffectedSelectivePermissionsInterventionView(this, this.#issue, 'selective-permissions-intervention-details'),
263
265
  ];
264
266
  this.#hiddenIssuesMenu = new Components.HideIssuesMenu.HideIssuesMenu();
265
267
  this.#aggregatedIssuesCount = null;
@@ -110,6 +110,10 @@ const UIStrings = {
110
110
  * @description Category title for a group of permission element issues
111
111
  */
112
112
  permissionElement: 'PEPC Element',
113
+ /**
114
+ * @description Category title for the different 'Selective Permissions Intervention' issues.
115
+ */
116
+ selectivePermissionsIntervention: 'Selective Permissions Intervention',
113
117
  } as const;
114
118
  const str_ = i18n.i18n.registerUIStrings('panels/issues/IssuesPane.ts', UIStrings);
115
119
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -153,6 +157,8 @@ class IssueCategoryView extends UI.TreeOutline.TreeElement {
153
157
  return i18nString(UIStrings.generic);
154
158
  case IssuesManager.Issue.IssueCategory.PERMISSION_ELEMENT:
155
159
  return i18nString(UIStrings.permissionElement);
160
+ case IssuesManager.Issue.IssueCategory.SELECTIVE_PERMISSIONS_INTERVENTION:
161
+ return i18nString(UIStrings.selectivePermissionsIntervention);
156
162
  case IssuesManager.Issue.IssueCategory.OTHER:
157
163
  return i18nString(UIStrings.other);
158
164
  }
@@ -2,11 +2,13 @@
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 AffectedSelectivePermissionsInterventionView from './AffectedSelectivePermissionsInterventionView.js';
5
6
  import * as IssueRevealer from './IssueRevealer.js';
6
7
  import * as IssuesPane from './IssuesPane.js';
7
8
  import * as IssueView from './IssueView.js';
8
9
 
9
10
  export {
11
+ AffectedSelectivePermissionsInterventionView,
10
12
  IssueRevealer,
11
13
  IssuesPane,
12
14
  IssueView,
@@ -11,6 +11,7 @@ import type * as Platform from '../../core/platform/platform.js';
11
11
  import * as Buttons from '../../ui/components/buttons/buttons.js';
12
12
  import {Link} from '../../ui/kit/kit.js';
13
13
  import * as UI from '../../ui/legacy/legacy.js';
14
+ import {Directives, html, render} from '../../ui/lit/lit.js';
14
15
 
15
16
  import {type LighthouseController, type Preset, Presets, RuntimeSettings} from './LighthouseController.js';
16
17
  import type {LighthousePanel} from './LighthousePanel.js';
@@ -55,6 +56,78 @@ const UIStrings = {
55
56
  const str_ = i18n.i18n.registerUIStrings('panels/lighthouse/LighthouseStartView.ts', UIStrings);
56
57
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
57
58
 
59
+ const renderStartView = (
60
+ _input: Record<string, never>,
61
+ output: {
62
+ helpText?: HTMLElement,
63
+ warningText?: HTMLElement,
64
+ modeFormElements?: HTMLElement,
65
+ deviceTypeFormElements?: HTMLElement,
66
+ categoriesFormElements?: HTMLElement,
67
+ },
68
+ target: HTMLElement,
69
+ ): void => {
70
+ // clang-format off
71
+ render(
72
+ html`
73
+ <form class="lighthouse-start-view">
74
+ <header class="hbox">
75
+ <div class="lighthouse-logo"></div>
76
+ <div class="lighthouse-title">
77
+ ${i18nString(UIStrings.generateLighthouseReport)}
78
+ </div>
79
+ <div class="lighthouse-start-button-container"></div>
80
+ </header>
81
+ <div
82
+ ${Directives.ref(e => {
83
+ output.helpText = e as HTMLElement;
84
+ })}
85
+ class="lighthouse-help-text hidden"
86
+ ></div>
87
+ <div class="lighthouse-options hbox">
88
+ <div class="lighthouse-form-section">
89
+ <div
90
+ class="lighthouse-form-elements"
91
+ ${Directives.ref(e => {
92
+ output.modeFormElements = e as HTMLElement;
93
+ })}
94
+ ></div>
95
+ </div>
96
+ <div class="lighthouse-form-section">
97
+ <div
98
+ class="lighthouse-form-elements"
99
+ ${Directives.ref(e => {
100
+ output.deviceTypeFormElements = e as HTMLElement;
101
+ })}
102
+ ></div>
103
+ </div>
104
+ <div class="lighthouse-form-categories">
105
+ <fieldset class="lighthouse-form-section lighthouse-form-categories-fieldset">
106
+ <legend class="lighthouse-form-section-label">
107
+ ${i18nString(UIStrings.categories)}
108
+ </legend>
109
+ <div
110
+ class="lighthouse-form-elements"
111
+ ${Directives.ref(e => {
112
+ output.categoriesFormElements = e as HTMLElement;
113
+ })}
114
+ ></div>
115
+ </fieldset>
116
+ </div>
117
+ </div>
118
+ <div
119
+ ${Directives.ref(e => {
120
+ output.warningText = e as HTMLElement;
121
+ })}
122
+ class="lighthouse-warning-text hidden"
123
+ ></div>
124
+ </form>
125
+ `,
126
+ target,
127
+ );
128
+ // clang-format on
129
+ };
130
+
58
131
  export class StartView extends UI.Widget.Widget {
59
132
  private controller: LighthouseController;
60
133
  private panel: LighthousePanel;
@@ -145,13 +218,11 @@ export class StartView extends UI.Widget.Widget {
145
218
  }
146
219
  }
147
220
 
148
- private populateFormControls(fragment: UI.Fragment.Fragment, mode?: string): void {
221
+ private populateFormControls(deviceTypeFormElements: Element, categoryFormElements: Element, mode?: string): void {
149
222
  // Populate the device type
150
- const deviceTypeFormElements = fragment.$('device-type-form-elements');
151
223
  this.populateRuntimeSettingAsRadio('lighthouse.device-type', i18nString(UIStrings.device), deviceTypeFormElements);
152
224
 
153
225
  // Populate the categories
154
- const categoryFormElements = fragment.$('categories-form-elements') as HTMLElement;
155
226
 
156
227
  this.checkboxes = [];
157
228
  for (const preset of Presets) {
@@ -174,44 +245,34 @@ export class StartView extends UI.Widget.Widget {
174
245
  this.populateRuntimeSettingAsToolbarDropdown('lighthouse.throttling', this.#settingsToolbar);
175
246
 
176
247
  const {mode} = this.controller.getFlags();
177
- this.populateStartButton(mode);
178
248
 
179
- const fragment = UI.Fragment.Fragment.build`
180
- <form class="lighthouse-start-view">
181
- <header class="hbox">
182
- <div class="lighthouse-logo"></div>
183
- <h1 class="lighthouse-title">${i18nString(UIStrings.generateLighthouseReport)}</h1>
184
- <div class="lighthouse-start-button-container" $="start-button-container">${this.startButton}</div>
185
- </header>
186
- <div $="help-text" class="lighthouse-help-text hidden"></div>
187
- <div class="lighthouse-options hbox">
188
- <div class="lighthouse-form-section">
189
- <div class="lighthouse-form-elements" $="mode-form-elements"></div>
190
- </div>
191
- <div class="lighthouse-form-section">
192
- <div class="lighthouse-form-elements" $="device-type-form-elements"></div>
193
- </div>
194
- <div class="lighthouse-form-categories">
195
- <fieldset class="lighthouse-form-section lighthouse-form-categories-fieldset">
196
- <legend class="lighthouse-form-section-label">${i18nString(UIStrings.categories)}</legend>
197
- <div class="lighthouse-form-elements" $="categories-form-elements"></div>
198
- </fieldset>
199
- </div>
200
- </div>
201
- <div $="warning-text" class="lighthouse-warning-text hidden"></div>
202
- </form>
203
- `;
204
-
205
- this.helpText = fragment.$('help-text');
206
- this.warningText = fragment.$('warning-text');
207
-
208
- const modeFormElements = fragment.$('mode-form-elements');
209
- this.populateRuntimeSettingAsRadio('lighthouse.mode', i18nString(UIStrings.mode), modeFormElements);
249
+ const output = {
250
+ helpText: undefined as HTMLElement | undefined,
251
+ warningText: undefined as HTMLElement | undefined,
252
+ modeFormElements: undefined as HTMLElement | undefined,
253
+ deviceTypeFormElements: undefined as HTMLElement | undefined,
254
+ categoriesFormElements: undefined as HTMLElement | undefined,
255
+ };
256
+
257
+ renderStartView(
258
+ {},
259
+ output,
260
+ this.contentElement,
261
+ );
210
262
 
211
- this.populateFormControls(fragment, mode);
263
+ this.helpText = output.helpText;
264
+ this.warningText = output.warningText;
212
265
 
213
- this.contentElement.textContent = '';
214
- this.contentElement.append(fragment.element());
266
+ const modeFormElements = output.modeFormElements;
267
+ const deviceTypeFormElements = output.deviceTypeFormElements;
268
+ const categoriesFormElements = output.categoriesFormElements;
269
+
270
+ if (!modeFormElements || !deviceTypeFormElements || !categoriesFormElements) {
271
+ throw new Error('Required elements not found in template');
272
+ }
273
+
274
+ this.populateRuntimeSettingAsRadio('lighthouse.mode', i18nString(UIStrings.mode), modeFormElements);
275
+ this.populateFormControls(deviceTypeFormElements, categoriesFormElements, mode);
215
276
 
216
277
  this.refresh();
217
278
  }
@@ -7,6 +7,7 @@ import * as i18n from '../../core/i18n/i18n.js';
7
7
  import * as Geometry from '../../models/geometry/geometry.js';
8
8
  import * as Buttons from '../../ui/components/buttons/buttons.js';
9
9
  import * as UI from '../../ui/legacy/legacy.js';
10
+ import {Directives, html, render} from '../../ui/lit/lit.js';
10
11
 
11
12
  import lighthouseDialogStyles from './lighthouseDialog.css.js';
12
13
  import type {LighthousePanel} from './LighthousePanel.js';
@@ -37,6 +38,43 @@ const UIStrings = {
37
38
  const str_ = i18n.i18n.registerUIStrings('panels/lighthouse/LighthouseTimespanView.ts', UIStrings);
38
39
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
39
40
 
41
+ const renderTimespanView = (
42
+ input: {
43
+ cancelButton: Element,
44
+ endButton: Element,
45
+ },
46
+ output: {
47
+ statusHeader?: HTMLElement,
48
+ contentContainer?: HTMLElement,
49
+ },
50
+ target: ShadowRoot,
51
+ ): void => {
52
+ // clang-format off
53
+ render(
54
+ html`
55
+ <div class="lighthouse-view vbox">
56
+ <span
57
+ ${Directives.ref(e => {
58
+ output.statusHeader = e as HTMLElement;
59
+ })}
60
+ class="header"
61
+ ></span>
62
+ <span
63
+ ${Directives.ref(e => {
64
+ output.contentContainer = e as HTMLElement;
65
+ })}
66
+ class="lighthouse-dialog-text"
67
+ ></span>
68
+ <div class="lighthouse-action-buttons hbox">
69
+ ${input.cancelButton} ${input.endButton}
70
+ </div>
71
+ </div>
72
+ `,
73
+ target,
74
+ );
75
+ // clang-format on
76
+ };
77
+
40
78
  export class TimespanView extends UI.Dialog.Dialog {
41
79
  private panel: LighthousePanel;
42
80
  private statusHeader: Element|null;
@@ -90,20 +128,21 @@ export class TimespanView extends UI.Dialog.Dialog {
90
128
  className: 'cancel',
91
129
  jslogContext: 'lighthouse.cancel',
92
130
  });
93
- const fragment = UI.Fragment.Fragment.build`
94
- <div class="lighthouse-view vbox">
95
- <span $="status-header" class="header"></span>
96
- <span $="call-to-action" class="lighthouse-dialog-text"></span>
97
- <div class="lighthouse-action-buttons hbox">
98
- ${cancelButton}
99
- ${this.endButton}
100
- </div>
101
- </div>
102
- `;
103
-
104
- this.statusHeader = fragment.$('status-header');
105
- this.contentContainer = fragment.$('call-to-action');
106
- dialogRoot.appendChild(fragment.element());
131
+
132
+ const output = {
133
+ statusHeader: undefined as HTMLElement | undefined,
134
+ contentContainer: undefined as HTMLElement | undefined,
135
+ };
136
+
137
+ renderTimespanView(
138
+ {
139
+ cancelButton,
140
+ endButton: this.endButton,
141
+ },
142
+ output, dialogRoot);
143
+
144
+ this.statusHeader = output.statusHeader ?? null;
145
+ this.contentContainer = output.contentContainer ?? null;
107
146
 
108
147
  this.setSizeBehavior(UI.GlassPane.SizeBehavior.SET_EXACT_WIDTH_MAX_HEIGHT);
109
148
  this.setMaxContentSize(new Geometry.Size(500, 400));