chrome-devtools-frontend 1.0.1701636 → 1.0.1703574

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 (91) hide show
  1. package/eslint.config.mjs +3 -1
  2. package/front_end/core/common/ParsedURL.ts +5 -1
  3. package/front_end/core/host/UserMetricsEnums.ts +2 -1
  4. package/front_end/core/root/ExperimentNames.ts +1 -0
  5. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -7
  6. package/front_end/core/text_utils/Markdown.ts +73 -0
  7. package/front_end/core/text_utils/text_utils.ts +2 -0
  8. package/front_end/devtools_compatibility.js +2 -1
  9. package/front_end/entrypoints/main/MainImpl.ts +8 -0
  10. package/front_end/generated/InspectorBackendCommands.ts +6 -4
  11. package/front_end/generated/protocol-mapping.d.ts +8 -0
  12. package/front_end/generated/protocol-proxy-api.d.ts +6 -0
  13. package/front_end/generated/protocol.ts +22 -3
  14. package/front_end/models/ai_assistance/AiAgent2.ts +13 -1
  15. package/front_end/models/ai_assistance/AiConversation.ts +1 -1
  16. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +13 -19
  17. package/front_end/models/change_tracker/ChangeTracker.ts +5 -64
  18. package/front_end/models/comment_manager/CD4ABridge.ts +5 -0
  19. package/front_end/models/comment_manager/CommentManager.ts +3 -5
  20. package/front_end/models/comment_manager/CommentThread.ts +30 -13
  21. package/front_end/models/issues_manager/CookieDeprecationMetadataIssue.ts +6 -7
  22. package/front_end/models/issues_manager/DeprecationIssue.ts +1 -1
  23. package/front_end/models/issues_manager/MarkdownIssueDescription.ts +18 -52
  24. package/front_end/models/issues_manager/SRIMessageSignatureIssue.ts +2 -4
  25. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantRead.md +0 -1
  26. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantReadOptOut.md +5 -0
  27. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantSet.md +0 -1
  28. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantSetOptOut.md +5 -0
  29. package/front_end/models/issues_manager/descriptions/deprecation.md +1 -1
  30. package/front_end/models/issues_manager/descriptions/sriValidationFailedIntegrityMismatch.md +2 -0
  31. package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +1 -1
  32. package/front_end/models/persistence/NetworkPersistenceManager.ts +14 -2
  33. package/front_end/panels/common/CommentThreadWidget.ts +29 -10
  34. package/front_end/panels/common/CommentsOverlayWidget.ts +50 -14
  35. package/front_end/panels/common/CommentsStatusBarPill.ts +40 -7
  36. package/front_end/panels/common/commentThreadWidget.css +11 -0
  37. package/front_end/panels/common/commentsStatusBarPill.css +13 -0
  38. package/front_end/panels/elements/StylePropertiesSection.ts +10 -0
  39. package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -1
  40. package/front_end/panels/elements/StylesSidebarPane.ts +101 -49
  41. package/front_end/panels/elements/components/CSSQuery.ts +1 -1
  42. package/front_end/panels/elements/components/cssQuery.css +4 -0
  43. package/front_end/panels/elements/elements.ts +0 -1
  44. package/front_end/panels/elements/stylesSidebarPane.css +6 -0
  45. package/front_end/panels/issues/IssueView.ts +5 -1
  46. package/front_end/panels/sources/ResourceOriginPlugin.ts +8 -3
  47. package/front_end/panels/sources/ScopeChainSidebarPane.ts +3 -4
  48. package/front_end/panels/sources/SourcesView.ts +127 -147
  49. package/front_end/panels/sources/TabbedEditorContainer.ts +518 -246
  50. package/front_end/panels/sources/UISourceCodeFrame.ts +9 -0
  51. package/front_end/third_party/chromium/README.chromium +1 -1
  52. package/front_end/third_party/chromium/client-variations/ClientVariations.js +25 -96
  53. package/front_end/third_party/codemirror.next/chunk/angular.js +1 -1
  54. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  55. package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
  56. package/front_end/third_party/codemirror.next/chunk/cpp.js +1 -1
  57. package/front_end/third_party/codemirror.next/chunk/cpp.js.map +1 -1
  58. package/front_end/third_party/codemirror.next/chunk/java.js +1 -1
  59. package/front_end/third_party/codemirror.next/chunk/java.js.map +1 -1
  60. package/front_end/third_party/codemirror.next/chunk/legacy.js +1 -1
  61. package/front_end/third_party/codemirror.next/chunk/less.js +1 -1
  62. package/front_end/third_party/codemirror.next/chunk/less.js.map +1 -1
  63. package/front_end/third_party/codemirror.next/chunk/markdown.js +1 -1
  64. package/front_end/third_party/codemirror.next/chunk/markdown.js.map +1 -1
  65. package/front_end/third_party/codemirror.next/chunk/php.js +1 -1
  66. package/front_end/third_party/codemirror.next/chunk/php.js.map +1 -1
  67. package/front_end/third_party/codemirror.next/chunk/python.js +1 -1
  68. package/front_end/third_party/codemirror.next/chunk/python.js.map +1 -1
  69. package/front_end/third_party/codemirror.next/chunk/sass.js +1 -1
  70. package/front_end/third_party/codemirror.next/chunk/sass.js.map +1 -1
  71. package/front_end/third_party/codemirror.next/chunk/svelte.js +1 -1
  72. package/front_end/third_party/codemirror.next/chunk/svelte.js.map +1 -1
  73. package/front_end/third_party/codemirror.next/chunk/vue.js +1 -1
  74. package/front_end/third_party/codemirror.next/chunk/wast.js +1 -1
  75. package/front_end/third_party/codemirror.next/chunk/wast.js.map +1 -1
  76. package/front_end/third_party/codemirror.next/chunk/xml.js +1 -1
  77. package/front_end/third_party/codemirror.next/chunk/xml.js.map +1 -1
  78. package/front_end/third_party/codemirror.next/codemirror.next.js +1 -1
  79. package/front_end/third_party/codemirror.next/package.json +4 -1
  80. package/front_end/third_party/codemirror.next/rebuild.sh +1 -1
  81. package/front_end/ui/comments/CommentOverlayManager.ts +11 -10
  82. package/front_end/ui/components/markdown_view/MarkdownPlaceholderLitRenderer.ts +59 -0
  83. package/front_end/ui/components/markdown_view/markdown_view.ts +2 -0
  84. package/front_end/ui/legacy/TabbedPane.ts +51 -40
  85. package/front_end/ui/legacy/Treeoutline.ts +28 -19
  86. package/front_end/ui/legacy/UIUtils.ts +1 -1
  87. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +424 -570
  88. package/front_end/ui/legacy/components/object_ui/objectPropertiesSection.css +2 -27
  89. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  90. package/mcp/mcp.ts +1 -0
  91. package/package.json +2 -1
@@ -4,16 +4,24 @@
4
4
 
5
5
  import * as i18n from '../../core/i18n/i18n.js';
6
6
  import * as CommentManager from '../../models/comment_manager/comment_manager.js';
7
+ import * as Buttons from '../../ui/components/buttons/buttons.js';
7
8
  import * as UI from '../../ui/legacy/legacy.js';
8
9
  import * as Lit from '../../ui/lit/lit.js';
9
10
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
10
11
 
12
+ import commentsStatusBarPillStyles from './commentsStatusBarPill.css.js';
13
+
11
14
  const UIStrings = {
12
15
  /**
13
16
  * @description Button text for the comments status bar pill showing the number of open comments.
14
17
  * @example {2} PH1
15
18
  */
16
19
  commentsCount: 'Comments ({PH1})',
20
+ /**
21
+ * @description Button text for sending all draft comments to the agent in the comments status bar pill.
22
+ * @example {2} PH1
23
+ */
24
+ sendToAgentCount: 'Send to agent ({PH1})',
17
25
  } as const;
18
26
  const str_ = i18n.i18n.registerUIStrings('panels/common/CommentsStatusBarPill.ts', UIStrings);
19
27
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -23,22 +31,38 @@ const {html, render} = Lit;
23
31
  export interface ViewInput {
24
32
  threads: CommentManager.CommentManager.CommentThread[];
25
33
  onPillClick: () => void;
34
+ onSendToAgentClick?: () => void;
26
35
  disabled?: boolean;
27
36
  }
28
37
 
29
38
  export type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
30
39
 
31
40
  export const DEFAULT_VIEW: View = (input: ViewInput, _output: undefined, target: HTMLElement): void => {
41
+ const unsentCount = input.threads.filter(thread => thread.status === 'ACTIVE').length;
42
+ const tooltip = input.threads.flatMap(thread => thread.comments.map(comment => comment.text)).join('\n');
32
43
  // clang-format off
33
44
  render(html`
45
+ <style>${commentsStatusBarPillStyles}</style>
34
46
  ${input.threads.length <= 0 ? Lit.nothing : html`
35
- <button
36
- class="devtools-pill"
37
- ?disabled=${input.disabled}
38
- @click=${input.onPillClick}
39
- jslog=${VisualLogging.action('comments-status-bar-pill').track({click: true})}>
40
- ${i18nString(UIStrings.commentsCount, {PH1: input.threads.length})}
41
- </button>
47
+ <div class="pill-container">
48
+ <button
49
+ class="devtools-pill"
50
+ title=${tooltip}
51
+ ?disabled=${input.disabled}
52
+ @click=${input.onPillClick}
53
+ jslog=${VisualLogging.action('comments-status-bar-pill').track({click: true})}>
54
+ ${i18nString(UIStrings.commentsCount, {PH1: input.threads.length})}
55
+ </button>
56
+ ${unsentCount > 0 ? html`
57
+ <devtools-button
58
+ .variant=${Buttons.Button.Variant.PRIMARY}
59
+ .disabled=${Boolean(input.disabled)}
60
+ .jslogContext=${'comments-send-to-agent'}
61
+ @click=${input.onSendToAgentClick}>
62
+ ${i18nString(UIStrings.sendToAgentCount, {PH1: unsentCount})}
63
+ </devtools-button>
64
+ ` : Lit.nothing}
65
+ </div>
42
66
  `}
43
67
  `, target);
44
68
  // clang-format on
@@ -100,9 +124,18 @@ export class CommentsStatusBarPill extends UI.Widget.Widget {
100
124
  this.#commentManager.getCommentThreads().filter(thread => thread.status !== 'DRAFT') :
101
125
  [],
102
126
  onPillClick: this.#handlePillClick,
127
+ onSendToAgentClick: this.#handleSendToAgentClick,
103
128
  };
104
129
  this.#view(viewInput, undefined, this.contentElement);
105
130
  }
106
131
 
107
132
  #handlePillClick = (): void => {};
133
+
134
+ #handleSendToAgentClick = (): void => {
135
+ for (const thread of this.#commentManager.getCommentThreads()) {
136
+ if (thread.status === 'ACTIVE') {
137
+ thread.sendToAgent();
138
+ }
139
+ }
140
+ };
108
141
  }
@@ -30,6 +30,17 @@
30
30
  gap: var(--sys-size-4);
31
31
  }
32
32
 
33
+ .header-actions {
34
+ display: flex;
35
+ align-items: center;
36
+ gap: var(--sys-size-3);
37
+ flex-shrink: 0;
38
+ }
39
+
40
+ .close-button {
41
+ flex-shrink: 0;
42
+ }
43
+
33
44
  .sent-status {
34
45
  display: flex;
35
46
  align-items: center;
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Copyright 2026 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
+ @scope to (devtools-widget > *) {
8
+ .pill-container {
9
+ display: flex;
10
+ align-items: center;
11
+ gap: var(--sys-size-3);
12
+ }
13
+ }
@@ -304,6 +304,7 @@ export class StylePropertiesSection {
304
304
  closeBrace.createChild('span').textContent = '}';
305
305
  } else {
306
306
  this.titleElement.classList.add('hidden');
307
+ this.updateAncestorRuleList();
307
308
  }
308
309
 
309
310
  if (rule) {
@@ -425,6 +426,7 @@ export class StylePropertiesSection {
425
426
  this.computedStyleExtraFields = computedStyleExtraFields;
426
427
  this.#lastInheritedNode = matchedStyles.isInherited(style) ? matchedStyles.nodeForStyle(style) : null;
427
428
  this.update(true);
429
+ this.updateCollapsedState();
428
430
  }
429
431
 
430
432
  inheritedNode(): SDK.DOMModel.DOMNode|null {
@@ -1250,6 +1252,14 @@ export class StylePropertiesSection {
1250
1252
  this.#ancestorClosingBracesElement.removeChildren();
1251
1253
  this.maybeCreateAncestorRules(this.styleInternal);
1252
1254
  this.#styleRuleElement.style.paddingLeft = `${this.nestingLevel}ch`;
1255
+ if (this.headerText().length === 0 && (this.styleInternal.parentRule instanceof SDK.CSSRule.CSSStyleRule)) {
1256
+ const lastAncestor = this.#ancestorRuleListElement.lastElementChild;
1257
+ if (lastAncestor && this.#collapseIcon && this.#statusElement) {
1258
+ this.#collapseIcon.slot = 'indent';
1259
+ lastAncestor.append(this.#collapseIcon, this.#statusElement);
1260
+ }
1261
+ this.#ancestorClosingBracesElement.firstElementChild?.classList.add('sidebar-pane-closing-brace');
1262
+ }
1253
1263
  }
1254
1264
 
1255
1265
  isPropertyInherited(propertyName: string): boolean {
@@ -2044,7 +2044,7 @@ export class AnchorFunctionRenderer extends AnchorFunctionRendererBase {
2044
2044
 
2045
2045
  override render(match: SDK.CSSPropertyParserMatchers.AnchorFunctionMatch, context: RenderingContext): Node[] {
2046
2046
  const content = document.createElement('span');
2047
- if (match.node.name !== 'CallExpression') {
2047
+ if (match.node.name === 'VariableName') {
2048
2048
  // Link an anchor double-dashed ident to its matching anchor element.
2049
2049
  void AnchorFunctionRenderer.decorateAnchorForAnchorLink(this.#stylesContainer, content, {identifier: match.text});
2050
2050
  } else {
@@ -1126,27 +1126,39 @@ export class StylesSidebarPane extends StylesSidebarPaneBase implements StylesCo
1126
1126
  }
1127
1127
  }
1128
1128
 
1129
- private getStyleId(style: SDK.CSSStyleDeclaration.CSSStyleDeclaration): string {
1129
+ private getStyleId(section: StylePropertiesSection): string {
1130
+ const style = section.styleInternal;
1131
+ const node = section.matchedStyles.isInherited(style) ? section.matchedStyles.nodeForStyle(style) :
1132
+ section.matchedStyles.node();
1133
+ const nodeId = node?.id ?? '';
1130
1134
  if (style.range) {
1131
- return `${style.styleSheetId || ''}:${style.range.toString()}`;
1135
+ return `${nodeId}:${style.styleSheetId || ''}:${style.range.toString()}`;
1132
1136
  }
1133
1137
  if (style.type === SDK.CSSStyleDeclaration.Type.Inline || style.type === SDK.CSSStyleDeclaration.Type.Attributes) {
1134
- return style.type;
1138
+ return `${nodeId}:${style.type}`;
1135
1139
  }
1136
1140
  if (style.type === SDK.CSSStyleDeclaration.Type.Animation) {
1137
- return `${style.type}:${style.animationName() || ''}:${style.cssText}`;
1141
+ return `${nodeId}:${style.type}:${style.animationName() || ''}:${style.cssText}`;
1138
1142
  }
1139
1143
  const parentRule = style.parentRule;
1140
1144
  if (parentRule instanceof SDK.CSSRule.CSSStyleRule) {
1141
- return `${style.type}:${parentRule.selectorText()}`;
1145
+ const ruleTypes = parentRule.ruleTypes.join(',');
1146
+ const nesting = parentRule.nestingSelectors?.join(',') ?? '';
1147
+ const media = parentRule.media.map(m => m.text).join(',');
1148
+ const containers = parentRule.containerQueries.map(c => c.text).join(',');
1149
+ const supports = parentRule.supports.map(s => s.text).join(',');
1150
+ const scopes = parentRule.scopes.map(s => s.text).join(',');
1151
+ const layers = parentRule.layers.map(l => l.text).join(',');
1152
+ return `${nodeId}:${style.type}:${parentRule.selectorText()}:${ruleTypes}:${nesting}:${media}:${containers}:${
1153
+ supports}:${scopes}:${layers}`;
1142
1154
  }
1143
1155
  if (parentRule instanceof SDK.CSSRule.CSSKeyframeRule) {
1144
- return `${style.type}:${parentRule.parentRuleName()}:${parentRule.key().text}`;
1156
+ return `${nodeId}:${style.type}:${parentRule.parentRuleName()}:${parentRule.key().text}`;
1145
1157
  }
1146
1158
  if (parentRule instanceof SDK.CSSRule.CSSPropertyRule) {
1147
- return `${style.type}:${parentRule.propertyName().text}`;
1159
+ return `${nodeId}:${style.type}:${parentRule.propertyName().text}`;
1148
1160
  }
1149
- return `${style.type}:${style.cssText}`;
1161
+ return `${nodeId}:${style.type}:${style.cssText}`;
1150
1162
  }
1151
1163
 
1152
1164
  rebuildSectionsForMatchedStyleRulesForTest(matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles,
@@ -1170,7 +1182,7 @@ export class StylesSidebarPane extends StylesSidebarPaneBase implements StylesCo
1170
1182
 
1171
1183
  this.idleCallbackManager = new IdleCallbackManager();
1172
1184
 
1173
- const blocks = [new SectionBlock(null)];
1185
+ const blocks = [new SectionBlock(null, undefined, undefined, 'main')];
1174
1186
  let sectionIdx = 0;
1175
1187
  let lastParentNode: SDK.DOMModel.DOMNode|null = null;
1176
1188
 
@@ -1183,7 +1195,7 @@ export class StylesSidebarPane extends StylesSidebarPaneBase implements StylesCo
1183
1195
  if (parentRule instanceof SDK.CSSRule.CSSStyleRule) {
1184
1196
  const layers = parentRule.layers;
1185
1197
  if ((layers.length || lastLayers) && lastLayers !== layers) {
1186
- const block = SectionBlock.createLayerBlock(parentRule);
1198
+ const block = SectionBlock.createLayerBlock(parentRule, lastLayerParent?.id);
1187
1199
  blocks.push(block);
1188
1200
  lastLayerParent?.childBlocks.push(block);
1189
1201
  sawLayers = true;
@@ -1409,37 +1421,36 @@ export class StylesSidebarPane extends StylesSidebarPaneBase implements StylesCo
1409
1421
  return blocks;
1410
1422
  }
1411
1423
 
1412
- return this.mergeInactiveStyles(blocks);
1424
+ return this.mergeInactiveStyles(blocks, matchedStyles, computedStyles, parentsComputedStyles,
1425
+ computedStyleExtraFields);
1413
1426
  }
1414
1427
 
1415
- private computeBlockIds(blocks: SectionBlock[]): Map<SectionBlock, string> {
1416
- let nullBlockCounter = 0;
1417
- const blockIds = new Map<SectionBlock, string>();
1418
- for (const block of blocks) {
1419
- blockIds.set(block, block.titleElement()?.textContent || `MAIN_BLOCK_NULL_${nullBlockCounter++}`);
1420
- }
1421
- return blockIds;
1422
- }
1423
-
1424
- private mergeInactiveStyles(blocks: SectionBlock[]): SectionBlock[] {
1425
- const blockIds = this.computeBlockIds(blocks);
1426
- for (const [id, block] of this.#allKnownBlocks) {
1427
- if (!blockIds.has(block)) {
1428
- blockIds.set(block, id);
1429
- }
1430
- }
1431
- const getBlockId = (block: SectionBlock): string => blockIds.get(block) || 'UNKNOWN_BLOCK';
1428
+ private mergeInactiveStyles(
1429
+ blocks: SectionBlock[],
1430
+ matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles,
1431
+ computedStyles: Map<string, string>|null,
1432
+ parentsComputedStyles: Map<string, string>|null,
1433
+ computedStyleExtraFields: Protocol.CSS.ComputedStyleExtraFields|null,
1434
+ ): SectionBlock[] {
1435
+ const getBlockId = (block: SectionBlock): string => block.id;
1432
1436
 
1433
1437
  for (const block of blocks) {
1434
1438
  const bid = getBlockId(block);
1435
1439
  const knownBlock = this.#allKnownBlocks.get(bid);
1436
1440
  if (knownBlock) {
1437
- block.sections = mergeOrderedItems(
1441
+ knownBlock.sections = mergeOrderedItems(
1438
1442
  knownBlock.sections,
1439
1443
  block.sections,
1440
- section => this.getStyleId(section.styleInternal),
1441
- section => section.setInactive(true),
1444
+ section => this.getStyleId(section),
1445
+ (section, active, newSection) => {
1446
+ if (active && newSection) {
1447
+ section.rebuildWithPayload(matchedStyles, newSection.style(), computedStyles, parentsComputedStyles,
1448
+ computedStyleExtraFields);
1449
+ }
1450
+ section.setInactive(!active);
1451
+ },
1442
1452
  );
1453
+ block.sections = knownBlock.sections;
1443
1454
  }
1444
1455
  }
1445
1456
 
@@ -1448,7 +1459,11 @@ export class StylesSidebarPane extends StylesSidebarPaneBase implements StylesCo
1448
1459
  oldBlocks,
1449
1460
  blocks,
1450
1461
  getBlockId,
1451
- block => block.sections.forEach(section => section.setInactive(true)),
1462
+ (block, active) => {
1463
+ if (!active) {
1464
+ block.sections.forEach(section => section.setInactive(true));
1465
+ }
1466
+ },
1452
1467
  );
1453
1468
 
1454
1469
  this.#allKnownBlocks.clear();
@@ -1456,6 +1471,19 @@ export class StylesSidebarPane extends StylesSidebarPaneBase implements StylesCo
1456
1471
  this.#allKnownBlocks.set(getBlockId(block), block);
1457
1472
  }
1458
1473
 
1474
+ // Restore child relationships using the new tree structure,
1475
+ // but pointing to the final instances.
1476
+ for (const newBlock of blocks) {
1477
+ const finalBlock = this.#allKnownBlocks.get(getBlockId(newBlock));
1478
+ if (finalBlock) {
1479
+ const newChildBlocks =
1480
+ newBlock.childBlocks.map(child => this.#allKnownBlocks.get(getBlockId(child))).filter(b => b !== undefined);
1481
+ finalBlock.childBlocks = finalBlock === newBlock ?
1482
+ newChildBlocks :
1483
+ mergeOrderedItems(finalBlock.childBlocks, newChildBlocks, getBlockId, () => {});
1484
+ }
1485
+ }
1486
+
1459
1487
  return finalBlocks;
1460
1488
  }
1461
1489
 
@@ -1871,7 +1899,7 @@ export class SectionBlock {
1871
1899
  childBlocks: SectionBlock[] = [];
1872
1900
  #expanded = false;
1873
1901
  #icon: Icon|undefined;
1874
- constructor(titleElement: Element|null, expandable?: boolean, expandedByDefault?: boolean) {
1902
+ constructor(titleElement: Element|null, expandable?: boolean, expandedByDefault?: boolean, readonly id = 'main') {
1875
1903
  this.#titleElement = titleElement;
1876
1904
  this.sections = [];
1877
1905
  this.#expanded = expandedByDefault ?? false;
@@ -1905,7 +1933,7 @@ export class SectionBlock {
1905
1933
  const pseudoArgumentString = pseudoArgument ? `(${pseudoArgument})` : '';
1906
1934
  const pseudoTypeString = `${pseudoType}${pseudoArgumentString}`;
1907
1935
  separatorElement.textContent = i18nString(UIStrings.pseudoSElement, {PH1: pseudoTypeString});
1908
- return new SectionBlock(separatorElement);
1936
+ return new SectionBlock(separatorElement, false, false, `pseudo:${pseudoType}:${pseudoArgument ?? ''}`);
1909
1937
  }
1910
1938
 
1911
1939
  static async createInheritedPseudoTypeBlock(pseudoType: Protocol.DOM.PseudoType, pseudoArgument: string|null,
@@ -1918,12 +1946,13 @@ export class SectionBlock {
1918
1946
  UI.UIUtils.createTextChild(separatorElement, i18nString(UIStrings.inheritedFromSPseudoOf, {PH1: pseudoTypeString}));
1919
1947
  const link = PanelsCommon.DOMLinkifier.Linkifier.instance().linkify(node, {preventKeyboardFocus: true});
1920
1948
  render(link, separatorElement);
1921
- return new SectionBlock(separatorElement);
1949
+ return new SectionBlock(separatorElement, false, false,
1950
+ `inherited-pseudo:${pseudoType}:${pseudoArgument ?? ''}:${node.id}`);
1922
1951
  }
1923
1952
 
1924
1953
  static createRegisteredPropertiesBlock(expandedByDefault: boolean): SectionBlock {
1925
1954
  const separatorElement = document.createElement('div');
1926
- const block = new SectionBlock(separatorElement, true, expandedByDefault);
1955
+ const block = new SectionBlock(separatorElement, true, expandedByDefault, 'registered-properties');
1927
1956
  separatorElement.className = 'sidebar-separator';
1928
1957
  separatorElement.appendChild(document.createTextNode(REGISTERED_PROPERTY_SECTION_NAME));
1929
1958
  return block;
@@ -1931,7 +1960,7 @@ export class SectionBlock {
1931
1960
 
1932
1961
  static createFunctionBlock(expandedByDefault: boolean): SectionBlock {
1933
1962
  const separatorElement = document.createElement('div');
1934
- const block = new SectionBlock(separatorElement, true, expandedByDefault);
1963
+ const block = new SectionBlock(separatorElement, true, expandedByDefault, 'functions');
1935
1964
  separatorElement.className = 'sidebar-separator';
1936
1965
  separatorElement.appendChild(document.createTextNode(FUNCTION_SECTION_NAME));
1937
1966
  return block;
@@ -1942,12 +1971,12 @@ export class SectionBlock {
1942
1971
  separatorElement.className = 'sidebar-separator';
1943
1972
  separatorElement.setAttribute('jslog', `${VisualLogging.sectionHeader('keyframes')}`);
1944
1973
  separatorElement.textContent = `@keyframes ${keyframesName}`;
1945
- return new SectionBlock(separatorElement);
1974
+ return new SectionBlock(separatorElement, false, false, `keyframes:${keyframesName}`);
1946
1975
  }
1947
1976
 
1948
1977
  static createAtRuleBlock(expandedByDefault: boolean): SectionBlock {
1949
1978
  const separatorElement = document.createElement('div');
1950
- const block = new SectionBlock(separatorElement, true, expandedByDefault);
1979
+ const block = new SectionBlock(separatorElement, true, expandedByDefault, 'at-rules');
1951
1980
  separatorElement.className = 'sidebar-separator';
1952
1981
  separatorElement.appendChild(document.createTextNode(i18nString(UIStrings.atRuleSection)));
1953
1982
  return block;
@@ -1958,7 +1987,7 @@ export class SectionBlock {
1958
1987
  separatorElement.className = 'sidebar-separator';
1959
1988
  separatorElement.setAttribute('jslog', `${VisualLogging.sectionHeader('position-try')}`);
1960
1989
  separatorElement.textContent = `@position-try ${positionTryName}`;
1961
- return new SectionBlock(separatorElement);
1990
+ return new SectionBlock(separatorElement, false, false, `position-try:${positionTryName}`);
1962
1991
  }
1963
1992
 
1964
1993
  static async createInheritedNodeBlock(node: SDK.DOMModel.DOMNode): Promise<SectionBlock> {
@@ -1970,10 +1999,10 @@ export class SectionBlock {
1970
1999
  preventKeyboardFocus: true,
1971
2000
  });
1972
2001
  render(link, separatorElement);
1973
- return new SectionBlock(separatorElement);
2002
+ return new SectionBlock(separatorElement, false, false, `inherited-node:${node.id}`);
1974
2003
  }
1975
2004
 
1976
- static createLayerBlock(rule: SDK.CSSRule.CSSStyleRule): SectionBlock {
2005
+ static createLayerBlock(rule: SDK.CSSRule.CSSStyleRule, parentBlockId = 'main'): SectionBlock {
1977
2006
  const separatorElement = document.createElement('div');
1978
2007
  separatorElement.className = 'sidebar-separator layer-separator';
1979
2008
  separatorElement.setAttribute('jslog', `${VisualLogging.sectionHeader('layer')}`);
@@ -1983,7 +2012,7 @@ export class SectionBlock {
1983
2012
  const name = rule.origin === Protocol.CSS.StyleSheetOrigin.UserAgent ? '\xa0user\xa0agent\xa0stylesheet' :
1984
2013
  '\xa0implicit\xa0outer\xa0layer';
1985
2014
  UI.UIUtils.createTextChild(separatorElement.createChild('div'), name);
1986
- return new SectionBlock(separatorElement);
2015
+ return new SectionBlock(separatorElement, false, false, `layer:${parentBlockId}:${name}`);
1987
2016
  }
1988
2017
  const layerLink = separatorElement.createChild('button');
1989
2018
  layerLink.className = 'link';
@@ -1991,7 +2020,7 @@ export class SectionBlock {
1991
2020
  const name = layers.map(layer => SDK.CSSModel.CSSModel.readableLayerName(layer.text)).join('.');
1992
2021
  layerLink.textContent = name;
1993
2022
  layerLink.onclick = () => LayersWidget.LayersWidget.instance().revealLayer(name);
1994
- return new SectionBlock(separatorElement);
2023
+ return new SectionBlock(separatorElement, false, false, `layer:${parentBlockId}:${name}`);
1995
2024
  }
1996
2025
 
1997
2026
  updateFilter(): number {
@@ -2701,30 +2730,53 @@ export function mergeOrderedItems<T>(
2701
2730
  oldItems: T[],
2702
2731
  newItems: T[],
2703
2732
  getId: (item: T) => string,
2704
- markInactive: (item: T) => void,
2733
+ toggleActive: (item: T, active: boolean, newItem?: T) => void,
2705
2734
  ): T[] {
2706
2735
  const newIds = new Set(newItems.map(getId));
2736
+ const oldItemById = new Map(oldItems.map(item => [getId(item), item]));
2737
+ const handledIds = new Set<string>();
2707
2738
  const merged: T[] = [];
2708
2739
  let newIdx = 0;
2709
2740
 
2710
2741
  for (const oldItem of oldItems) {
2711
2742
  const oldId = getId(oldItem);
2743
+ if (handledIds.has(oldId)) {
2744
+ continue;
2745
+ }
2712
2746
  if (newIds.has(oldId)) {
2713
2747
  while (newIdx < newItems.length) {
2714
2748
  const newItem = newItems[newIdx++];
2715
- merged.push(newItem);
2716
- if (getId(newItem) === oldId) {
2749
+ const newId = getId(newItem);
2750
+ const existingItem = oldItemById.get(newId);
2751
+ if (existingItem) {
2752
+ toggleActive(existingItem, true, newItem);
2753
+ merged.push(existingItem);
2754
+ handledIds.add(newId);
2755
+ } else {
2756
+ merged.push(newItem);
2757
+ }
2758
+ if (newId === oldId) {
2717
2759
  break;
2718
2760
  }
2719
2761
  }
2720
2762
  } else {
2721
- markInactive(oldItem);
2763
+ toggleActive(oldItem, false);
2722
2764
  merged.push(oldItem);
2765
+ handledIds.add(oldId);
2723
2766
  }
2724
2767
  }
2725
2768
 
2726
2769
  while (newIdx < newItems.length) {
2727
- merged.push(newItems[newIdx++]);
2770
+ const newItem = newItems[newIdx++];
2771
+ const newId = getId(newItem);
2772
+ const existingItem = oldItemById.get(newId);
2773
+ if (existingItem && !handledIds.has(newId)) {
2774
+ toggleActive(existingItem, true, newItem);
2775
+ merged.push(existingItem);
2776
+ handledIds.add(newId);
2777
+ } else if (!handledIds.has(newId)) {
2778
+ merged.push(newItem);
2779
+ }
2728
2780
  }
2729
2781
 
2730
2782
  return merged;
@@ -171,7 +171,7 @@ export class CSSQuery extends HTMLElement {
171
171
  <slot name="indent"></slot>
172
172
  ${this.#queryPrefix ? html`<span>${this.#queryPrefix + ' '}</span>` : Lit.nothing}
173
173
  ${this.#queryName ? html`<span>${this.#queryName + ' '}</span>` : Lit.nothing}
174
- ${queryText} {
174
+ ${queryText}<slot></slot> <span class="sidebar-pane-open-brace">{</span>
175
175
  </div>`,
176
176
  this.#shadow, {host: this});
177
177
  // clang-format on
@@ -16,3 +16,7 @@
16
16
  text-decoration: var(--override-styles-section-text-hover-text-decoration);
17
17
  cursor: var(--override-styles-section-text-hover-cursor);
18
18
  }
19
+
20
+ :host-context(.collapsed):host(:has(devtools-icon)) .sidebar-pane-open-brace::after {
21
+ content: "}";
22
+ }
@@ -81,7 +81,6 @@ export {
81
81
  DOMPath,
82
82
  DOMTreeContextMenu,
83
83
  DOMTreeWidget,
84
- DOMTreeWidget as ElementsTreeOutline,
85
84
  ElementsPanel,
86
85
  ElementsSidebarPane,
87
86
  ElementStatePaneWidget,
@@ -115,6 +115,7 @@
115
115
  & .section-collapse-icon {
116
116
  width: var(--sys-size-6);
117
117
  height: var(--sys-size-6);
118
+ margin-block: calc(-1 * var(--sys-size-3));
118
119
  margin-right: var(--sys-size-2);
119
120
  margin-left: calc(-1 * var(--sys-size-2));
120
121
  vertical-align: middle;
@@ -122,6 +123,11 @@
122
123
  display: none;
123
124
  }
124
125
 
126
+ & .styles-section-status {
127
+ margin-block: calc(-1 * var(--sys-size-4));
128
+ vertical-align: middle;
129
+ }
130
+
125
131
  &.collapsible .section-collapse-icon {
126
132
  display: inline-block;
127
133
  }
@@ -435,7 +435,11 @@ export class IssueView extends UI.TreeOutline.TreeElement {
435
435
  messageElement.setCollapsible(false);
436
436
  messageElement.selectable = false;
437
437
  const markdownComponent = new MarkdownView.MarkdownView.MarkdownView();
438
- markdownComponent.data = {tokens: this.#description.markdown};
438
+ markdownComponent.data = {
439
+ tokens: this.#description.markdown,
440
+ renderer: new MarkdownView.MarkdownPlaceholderLitRenderer.MarkdownPlaceholderLitRenderer(
441
+ this.#description.substitutions),
442
+ };
439
443
  messageElement.listItemElement.appendChild(markdownComponent);
440
444
  UI.ARIAUtils.setPositionInSet(messageElement.listItemElement, 1);
441
445
  UI.ARIAUtils.setSetSize(messageElement.listItemElement, this.#description.links.length === 0 ? 2 : 3);
@@ -49,11 +49,15 @@ export class ResourceOriginPlugin extends Plugin {
49
49
  const url = uiSourceCode.url();
50
50
  const text = Bindings.ResourceUtils.displayNameForURL(url);
51
51
  const title = i18nString(UIStrings.sourceMappedFromS, {PH1: text});
52
- links.push(Components.Linkifier.Linkifier.linkifyRevealable(
53
- uiSourceCode, text, url, title, undefined, 'original-script-location'));
52
+ const link = Components.Linkifier.Linkifier.linkifyRevealable(uiSourceCode, text, url, title, undefined,
53
+ 'original-script-location');
54
+ link.tabIndex = 0;
55
+ links.push(link);
54
56
  }
55
57
  for (const originURL of Bindings.SASSSourceMapping.SASSSourceMapping.uiSourceOrigin(this.uiSourceCode)) {
56
- links.push(Components.Linkifier.Linkifier.linkifyURL(originURL));
58
+ const link = Components.Linkifier.Linkifier.linkifyURL(originURL);
59
+ link.tabIndex = 0;
60
+ links.push(link);
57
61
  }
58
62
  if (links.length === 0) {
59
63
  return [];
@@ -72,6 +76,7 @@ export class ResourceOriginPlugin extends Plugin {
72
76
  for (const script of debuggerWorkspaceBinding.scriptsForUISourceCode(this.uiSourceCode)) {
73
77
  if (script.originStackTrace?.callFrames.length) {
74
78
  const link = this.#linkifier.linkifyStackTraceTopFrame(script.debuggerModel.target(), script.originStackTrace);
79
+ link.tabIndex = 0;
75
80
  return [new UI.Toolbar.ToolbarItem(uiI18n.getFormatLocalizedString(str_, UIStrings.fromS, {PH1: link}))];
76
81
  }
77
82
  }
@@ -28,7 +28,6 @@
28
28
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
  */
30
30
 
31
- import type * as Common from '../../core/common/common.js';
32
31
  import * as i18n from '../../core/i18n/i18n.js';
33
32
  import type * as SDK from '../../core/sdk/sdk.js';
34
33
  import * as Protocol from '../../generated/protocol.js';
@@ -38,7 +37,7 @@ import * as StackTrace from '../../models/stack_trace/stack_trace.js';
38
37
  import * as ObjectUI from '../../ui/legacy/components/object_ui/object_ui.js';
39
38
  import * as Components from '../../ui/legacy/components/utils/utils.js';
40
39
  import * as UI from '../../ui/legacy/legacy.js';
41
- import {html, nothing, render, type TemplateResult} from '../../ui/lit/lit.js';
40
+ import {html, type LitTemplate, nothing, render, type TemplateResult} from '../../ui/lit/lit.js';
42
41
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
43
42
 
44
43
  import scopeChainSidebarPaneStyles from './scopeChainSidebarPane.css.js';
@@ -89,9 +88,9 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
89
88
  scope: SDK.DebuggerModel.ScopeChainEntry,
90
89
  objectTree: ObjectUI.ObjectPropertiesSection.ObjectTree,
91
90
  }): TemplateResult => {
92
- let emptyPlaceholder: Common.UIString.LocalizedString|undefined;
91
+ let emptyPlaceholder: LitTemplate|undefined;
93
92
  if (scope.type() === Protocol.Debugger.ScopeType.Local || scope.type() === Protocol.Debugger.ScopeType.Closure) {
94
- emptyPlaceholder = i18nString(UIStrings.noVariables);
93
+ emptyPlaceholder = html`${i18nString(UIStrings.noVariables)}`;
95
94
  }
96
95
  const icon = scope.icon();
97
96
  const {title, subtitle} = scopeTitle(scope);