chrome-devtools-frontend 1.0.1583146 → 1.0.1585664

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 (119) hide show
  1. package/front_end/core/host/InspectorFrontendHostAPI.ts +1 -0
  2. package/front_end/core/host/InspectorFrontendHostStub.ts +2 -0
  3. package/front_end/core/root/Runtime.ts +12 -6
  4. package/front_end/core/sdk/CSSMatchedStyles.ts +3 -0
  5. package/front_end/core/sdk/CSSMetadata.ts +3 -0
  6. package/front_end/entrypoints/main/MainImpl.ts +1 -0
  7. package/front_end/generated/Deprecation.ts +21 -0
  8. package/front_end/generated/InspectorBackendCommands.ts +5 -3
  9. package/front_end/generated/SupportedCSSProperties.js +10 -4
  10. package/front_end/generated/protocol-mapping.d.ts +2 -1
  11. package/front_end/generated/protocol-proxy-api.d.ts +2 -1
  12. package/front_end/generated/protocol.ts +14 -0
  13. package/front_end/models/ai_assistance/AiConversation.ts +34 -2
  14. package/front_end/models/ai_assistance/BuiltInAi.ts +1 -2
  15. package/front_end/models/ai_assistance/agents/AiAgent.ts +3 -10
  16. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +1 -1
  17. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +17 -9
  18. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +0 -24
  19. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +12 -0
  20. package/front_end/models/ai_code_generation/AiCodeGeneration.ts +5 -0
  21. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +16 -8
  22. package/front_end/models/computed_style/ComputedStyleModel.ts +20 -15
  23. package/front_end/models/greendev/Prototypes.ts +1 -5
  24. package/front_end/models/issues_manager/CorsIssue.ts +2 -9
  25. package/front_end/models/issues_manager/descriptions/corsLocalNetworkAccessPermissionDenied.md +2 -2
  26. package/front_end/models/javascript_metadata/NativeFunctions.js +20 -0
  27. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +13 -36
  28. package/front_end/panels/ai_assistance/components/ChatInput.ts +73 -95
  29. package/front_end/panels/ai_assistance/components/ChatView.ts +13 -2
  30. package/front_end/panels/application/FrameDetailsView.ts +4 -9
  31. package/front_end/panels/common/AiCodeGenerationTeaser.ts +10 -0
  32. package/front_end/panels/console/ConsoleView.ts +1 -2
  33. package/front_end/panels/console/ConsoleViewMessage.ts +19 -12
  34. package/front_end/panels/elements/ComputedStyleWidget.ts +41 -21
  35. package/front_end/panels/elements/ElementsPanel.ts +18 -5
  36. package/front_end/panels/elements/ElementsTreeElement.ts +173 -169
  37. package/front_end/panels/elements/NodeStackTraceWidget.ts +3 -13
  38. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +69 -0
  39. package/front_end/panels/elements/computedStyleWidget.css +1 -6
  40. package/front_end/panels/elements/elements.ts +3 -1
  41. package/front_end/panels/issues/AffectedResourcesView.ts +1 -2
  42. package/front_end/panels/network/NetworkDataGridNode.ts +0 -8
  43. package/front_end/panels/network/RequestConditionsDrawer.ts +138 -112
  44. package/front_end/panels/network/RequestInitiatorView.ts +2 -10
  45. package/front_end/panels/settings/SettingsScreen.ts +12 -4
  46. package/front_end/panels/snippets/SnippetsQuickOpen.ts +6 -9
  47. package/front_end/panels/sources/CallStackSidebarPane.ts +4 -6
  48. package/front_end/panels/sources/DebuggerPlugin.ts +2 -1
  49. package/front_end/panels/sources/GoToLineQuickOpen.ts +0 -4
  50. package/front_end/panels/sources/OpenFileQuickOpen.ts +0 -4
  51. package/front_end/panels/sources/OutlineQuickOpen.ts +0 -4
  52. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +3 -1
  53. package/front_end/panels/sources/sources-meta.ts +9 -6
  54. package/front_end/panels/timeline/TimelineFlameChartView.ts +0 -12
  55. package/front_end/panels/timeline/TimelinePanel.ts +35 -15
  56. package/front_end/panels/timeline/TimelineUIUtils.ts +2 -6
  57. package/front_end/panels/timeline/components/Sidebar.ts +21 -0
  58. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +1 -20
  59. package/front_end/panels/timeline/components/insights/InsightRenderer.ts +0 -1
  60. package/front_end/third_party/chromium/README.chromium +1 -1
  61. package/front_end/third_party/puppeteer/README.chromium +2 -2
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.d.ts +1 -1
  63. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.d.ts.map +1 -1
  64. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.js +1 -1
  65. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.js.map +1 -1
  66. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  67. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js +2 -3
  68. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js.map +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  70. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  71. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/encoding.d.ts.map +1 -1
  72. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/encoding.js +5 -3
  73. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/encoding.js.map +1 -1
  74. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  75. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  76. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +8 -6
  77. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.d.ts +1 -1
  78. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.d.ts.map +1 -1
  79. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.js +1 -1
  80. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.js.map +1 -1
  81. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  82. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js +2 -3
  83. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js.map +1 -1
  84. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  85. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  86. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/encoding.d.ts.map +1 -1
  87. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/encoding.js +5 -3
  88. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/encoding.js.map +1 -1
  89. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  90. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  91. package/front_end/third_party/puppeteer/package/package.json +4 -4
  92. package/front_end/third_party/puppeteer/package/src/bidi/Page.ts +1 -1
  93. package/front_end/third_party/puppeteer/package/src/bidi/core/BrowsingContext.ts +2 -2
  94. package/front_end/third_party/puppeteer/package/src/node/ChromeLauncher.ts +4 -3
  95. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  96. package/front_end/third_party/puppeteer/package/src/util/encoding.ts +5 -3
  97. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  98. package/front_end/ui/components/linkifier/LinkifierImpl.ts +3 -0
  99. package/front_end/ui/components/linkifier/LinkifierUtils.ts +3 -0
  100. package/front_end/ui/components/text_editor/AiCodeGenerationParser.ts +29 -7
  101. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +34 -18
  102. package/front_end/ui/legacy/InspectorView.ts +41 -15
  103. package/front_end/ui/legacy/UIUtils.ts +30 -6
  104. package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +4 -3
  105. package/front_end/ui/legacy/components/quick_open/FilteredListWidget.ts +2 -1
  106. package/front_end/ui/legacy/components/quick_open/HelpQuickOpen.ts +8 -2
  107. package/front_end/ui/legacy/components/quick_open/QuickOpen.ts +8 -8
  108. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +20 -150
  109. package/front_end/ui/legacy/components/utils/Linkifier.ts +120 -87
  110. package/front_end/ui/legacy/components/utils/jsUtils.css +0 -9
  111. package/front_end/ui/legacy/legacy.ts +0 -2
  112. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  113. package/mcp/HostBindings.ts +3 -0
  114. package/package.json +1 -1
  115. package/front_end/models/issues_manager/descriptions/corsInsecurePrivateNetwork.md +0 -10
  116. package/front_end/models/issues_manager/descriptions/corsPreflightAllowPrivateNetworkError.md +0 -10
  117. package/front_end/models/issues_manager/descriptions/corsPrivateNetworkPermissionDenied.md +0 -10
  118. package/front_end/ui/legacy/Floaty.ts +0 -438
  119. package/front_end/ui/legacy/floaty.css +0 -77
@@ -54,7 +54,7 @@ import type * as Adorners from '../../ui/components/adorners/adorners.js';
54
54
  import * as CodeHighlighter from '../../ui/components/code_highlighter/code_highlighter.js';
55
55
  import * as Highlighting from '../../ui/components/highlighting/highlighting.js';
56
56
  import * as TextEditor from '../../ui/components/text_editor/text_editor.js';
57
- import {Icon, Link} from '../../ui/kit/kit.js';
57
+ import {Icon} from '../../ui/kit/kit.js';
58
58
  import * as Components from '../../ui/legacy/components/utils/utils.js';
59
59
  import * as UI from '../../ui/legacy/legacy.js';
60
60
  import * as Lit from '../../ui/lit/lit.js';
@@ -72,7 +72,8 @@ import {type ElementsTreeOutline, MappedCharToEntity} from './ElementsTreeOutlin
72
72
  import {ImagePreviewPopover} from './ImagePreviewPopover.js';
73
73
  import {getRegisteredDecorators, type MarkerDecorator, type MarkerDecoratorRegistration} from './MarkerDecorator.js';
74
74
 
75
- const {html, nothing, render, Directives: {ref}} = Lit;
75
+ const {html, nothing, render, Directives: {ref, repeat}} = Lit;
76
+ const {animateOn} = UI.UIUtils;
76
77
 
77
78
  const UIStrings = {
78
79
  /**
@@ -465,6 +466,8 @@ export interface Decoration {
465
466
  color: string;
466
467
  }
467
468
 
469
+ const DOM_UPDATE_ANIMATION_CLASS_NAME = 'dom-update-highlight';
470
+
468
471
  function handleAdornerKeydown(cb: (event: Event) => void): (event: KeyboardEvent) => void {
469
472
  return (event: KeyboardEvent) => {
470
473
  if (event.code === 'Enter' || event.code === 'Space') {
@@ -525,14 +528,6 @@ function renderTitle(
525
528
  const result = convertUnicodeCharsToHTMLEntities(firstChild.nodeValue());
526
529
  const textContent = Platform.StringUtilities.collapseWhitespace(result.text);
527
530
 
528
- const highlightAnimation = (updateRecord?.hasChangedChildren() || updateRecord?.isCharDataModified()) ?
529
- ref(el => {
530
- if (el) {
531
- UI.UIUtils.runCSSAnimationOnce(el, 'dom-update-highlight');
532
- }
533
- }) :
534
- nothing;
535
-
536
531
  const renderTextNode = ref(el => {
537
532
  if (el) {
538
533
  el.textContent = textContent;
@@ -541,8 +536,11 @@ function renderTitle(
541
536
  });
542
537
 
543
538
  return html`${openingTag}<span class="webkit-html-text-node" jslog=${
544
- VisualLogging.value('text-node').track({change: true, dblclick: true})} ${highlightAnimation} ${
545
- renderTextNode}></span>\u200B${renderTag(node, tagName, true, expanded, false, updateRecord)}`;
539
+ VisualLogging.value('text-node').track({change: true, dblclick: true})} ${
540
+ animateOn(
541
+ Boolean((updateRecord?.hasChangedChildren() || updateRecord?.isCharDataModified())),
542
+ DOM_UPDATE_ANIMATION_CLASS_NAME)} ${renderTextNode}></span>\u200B${
543
+ renderTag(node, tagName, true, expanded, false, updateRecord)}`;
546
544
  }
547
545
 
548
546
  if (isXMLMimeType || !ForbiddenClosingTagElements.has(tagName)) {
@@ -577,12 +575,6 @@ function renderTitle(
577
575
 
578
576
  const result = convertUnicodeCharsToHTMLEntities(node.nodeValue());
579
577
  const textContent = Platform.StringUtilities.collapseWhitespace(result.text);
580
- const highlightAnimation = updateRecord?.isCharDataModified() ? ref(el => {
581
- if (el) {
582
- UI.UIUtils.runCSSAnimationOnce(el, 'dom-update-highlight');
583
- }
584
- }) :
585
- nothing;
586
578
  const renderTextNode = ref(el => {
587
579
  if (el) {
588
580
  el.textContent = textContent;
@@ -592,7 +584,8 @@ function renderTitle(
592
584
  return html`"<span class="webkit-html-text-node" jslog=${VisualLogging.value('text-node').track({
593
585
  change: true,
594
586
  dblclick: true
595
- })} ${highlightAnimation} ${renderTextNode}></span>"`;
587
+ })} ${animateOn(Boolean(updateRecord?.isCharDataModified()), DOM_UPDATE_ANIMATION_CLASS_NAME)} ${
588
+ renderTextNode}></span>"`;
596
589
  }
597
590
 
598
591
  case Node.COMMENT_NODE: {
@@ -622,18 +615,12 @@ function renderTitle(
622
615
 
623
616
  case Node.DOCUMENT_NODE: {
624
617
  const text = (node as SDK.DOMModel.DOMDocument).documentURL;
625
- const linkify = ref(el => {
626
- if (el) {
627
- el.removeChildren();
628
- el.appendChild(Components.Linkifier.Linkifier.linkifyURL(text, {
629
- text,
630
- preventClick: true,
631
- showColumnNumber: false,
632
- inlineFrameIndex: 0,
633
- }));
634
- }
635
- });
636
- return html`<span>#document (<span ${linkify}></span>)</span>`;
618
+ return html`<span>#document (<span>${Components.Linkifier.Linkifier.renderLinkifiedUrl(text, {
619
+ text,
620
+ preventClick: true,
621
+ showColumnNumber: false,
622
+ inlineFrameIndex: 0,
623
+ })}</span>)</span>`;
637
624
  }
638
625
 
639
626
  case Node.DOCUMENT_FRAGMENT_NODE: {
@@ -647,138 +634,138 @@ function renderTitle(
647
634
  }
648
635
  }
649
636
 
650
- function renderAttribute(
651
- attr: {name: string, value?: string}, updateRecord: Elements.ElementUpdateRecord.ElementUpdateRecord|null,
652
- isDiff: boolean, node: SDK.DOMModel.DOMNode): Lit.LitTemplate {
653
- const name = attr.name;
654
- const value = attr.value || '';
655
- const forceValue = isDiff;
656
- const closingPunctuationRegex = /[\/;:\)\]\}]/g;
657
- let highlightIndex = 0;
658
- let highlightCount = 0;
659
- let additionalHighlightOffset = 0;
637
+ const enum SrcsetTokenType {
638
+ LITERAL = 0,
639
+ LINK = 1
640
+ }
660
641
 
661
- function setValueWithEntities(element: Element, value: string): void {
662
- const result = convertUnicodeCharsToHTMLEntities(value);
663
- highlightCount = result.entityRanges.length;
664
- const newValue = result.text.replace(closingPunctuationRegex, (match, replaceOffset) => {
665
- while (highlightIndex < highlightCount && result.entityRanges[highlightIndex].offset < replaceOffset) {
666
- result.entityRanges[highlightIndex].offset += additionalHighlightOffset;
667
- ++highlightIndex;
642
+ interface SrcsetToken {
643
+ type: SrcsetTokenType;
644
+ value: string;
645
+ }
646
+
647
+ // FIXME: find a home for this in SDK.
648
+ function parseSrcset(value: string): SrcsetToken[] {
649
+ const result: SrcsetToken[] = [];
650
+ let i = 0;
651
+ while (value.length) {
652
+ if (i++ > 0) {
653
+ result.push({value: ' ', type: SrcsetTokenType.LITERAL});
654
+ }
655
+ value = value.trim();
656
+ let url = '';
657
+ let descriptor = '';
658
+ const indexOfSpace = value.search(/\s/);
659
+ if (indexOfSpace === -1) {
660
+ url = value;
661
+ } else if (indexOfSpace > 0 && value[indexOfSpace - 1] === ',') {
662
+ url = value.substring(0, indexOfSpace);
663
+ } else {
664
+ url = value.substring(0, indexOfSpace);
665
+ const indexOfComma = value.indexOf(',', indexOfSpace);
666
+ if (indexOfComma !== -1) {
667
+ descriptor = value.substring(indexOfSpace, indexOfComma + 1);
668
+ } else {
669
+ descriptor = value.substring(indexOfSpace);
668
670
  }
669
- additionalHighlightOffset += 1;
670
- return match + '\u200B';
671
- });
671
+ }
672
672
 
673
- while (highlightIndex < highlightCount) {
674
- result.entityRanges[highlightIndex].offset += additionalHighlightOffset;
675
- ++highlightIndex;
673
+ if (url) {
674
+ if (url.endsWith(',')) {
675
+ result.push({value: url.substring(0, url.length - 1), type: SrcsetTokenType.LINK});
676
+ result.push({type: SrcsetTokenType.LITERAL, value: ','});
677
+ } else {
678
+ result.push({value: url, type: SrcsetTokenType.LINK});
679
+ }
680
+ }
681
+ if (descriptor) {
682
+ result.push({type: SrcsetTokenType.LITERAL, value: descriptor});
676
683
  }
677
- element.setTextContentTruncatedIfNeeded(newValue);
678
- Highlighting.highlightRangesWithStyleClass(element, result.entityRanges, 'webkit-html-entity-value');
684
+ value = value.substring(url.length + descriptor.length);
679
685
  }
686
+ return result;
687
+ }
680
688
 
681
- const hasText = (forceValue || value.length > 0);
682
- const jslog = VisualLogging.value(name === 'style' ? 'style-attribute' : 'attribute').track({
683
- change: true,
684
- dblclick: true,
685
- });
686
-
687
- const highlightNameAnimation = updateRecord?.isAttributeModified(name) && !hasText ? ref(el => {
688
- if (el) {
689
- UI.UIUtils.runCSSAnimationOnce(el, 'dom-update-highlight');
689
+ function renderLinkifiedSrcset(tokens: SrcsetToken[], node: SDK.DOMModel.DOMNode): Lit.TemplateResult {
690
+ return html`${repeat(tokens, token => {
691
+ switch (token.type) {
692
+ case SrcsetTokenType.LINK:
693
+ return renderLinkifiedValue(token.value, node);
694
+ case SrcsetTokenType.LITERAL:
695
+ return token.value;
690
696
  }
691
- }) :
692
- nothing;
697
+ })}`;
698
+ }
693
699
 
694
- const highlightValueAnimation = updateRecord?.isAttributeModified(name) && hasText ? ref(el => {
695
- if (el) {
696
- UI.UIUtils.runCSSAnimationOnce(el, 'dom-update-highlight');
700
+ const closingPunctuationRegex = /[\/;:\)\]\}]/g;
701
+
702
+ // FIXME: this should be made declarative next.
703
+ function setValueWithEntities(element: Element, value: string): void {
704
+ let highlightIndex = 0;
705
+ let highlightCount = 0;
706
+ let additionalHighlightOffset = 0;
707
+ const result = convertUnicodeCharsToHTMLEntities(value);
708
+ highlightCount = result.entityRanges.length;
709
+ const newValue = result.text.replace(closingPunctuationRegex, (match, replaceOffset) => {
710
+ while (highlightIndex < highlightCount && result.entityRanges[highlightIndex].offset < replaceOffset) {
711
+ result.entityRanges[highlightIndex].offset += additionalHighlightOffset;
712
+ ++highlightIndex;
697
713
  }
698
- }) :
699
- nothing;
700
-
701
- function linkifyValue(value: string): Element {
702
- const rewrittenHref = node ? node.resolveURL(value) : null;
703
- if (rewrittenHref === null) {
704
- const span = document.createElement('span');
705
- setValueWithEntities(span, value);
706
- return span;
707
- }
708
- value = value.replace(closingPunctuationRegex, '$&\u200B');
709
- if (value.startsWith('data:')) {
710
- value = Platform.StringUtilities.trimMiddle(value, 60);
711
- }
712
- const link = node && node.nodeName().toLowerCase() === 'a' ?
713
- Link.create(rewrittenHref, value, undefined, 'image-url') :
714
- Components.Linkifier.Linkifier.linkifyURL(rewrittenHref, {
715
- text: value,
716
- preventClick: true,
717
- showColumnNumber: false,
718
- inlineFrameIndex: 0,
719
- });
720
- return ImagePreviewPopover.setImageUrl(link, rewrittenHref);
721
- }
722
-
723
- function linkifySrcset(value: string): DocumentFragment {
724
- // Splitting normally on commas or spaces will break on valid srcsets "foo 1x,bar 2x" and "data:,foo 1x".
725
- const fragment = document.createDocumentFragment();
726
- let i = 0;
727
- while (value.length) {
728
- if (i++ > 0) {
729
- UI.UIUtils.createTextChild(fragment, ' ');
730
- }
731
- value = value.trim();
732
- let url = '';
733
- let descriptor = '';
734
- const indexOfSpace = value.search(/\s/);
735
- if (indexOfSpace === -1) {
736
- url = value;
737
- } else if (indexOfSpace > 0 && value[indexOfSpace - 1] === ',') {
738
- url = value.substring(0, indexOfSpace);
739
- } else {
740
- url = value.substring(0, indexOfSpace);
741
- const indexOfComma = value.indexOf(',', indexOfSpace);
742
- if (indexOfComma !== -1) {
743
- descriptor = value.substring(indexOfSpace, indexOfComma + 1);
744
- } else {
745
- descriptor = value.substring(indexOfSpace);
746
- }
747
- }
714
+ additionalHighlightOffset += 1;
715
+ return match + '\u200B';
716
+ });
748
717
 
749
- if (url) {
750
- if (url.endsWith(',')) {
751
- fragment.appendChild(linkifyValue(url.substring(0, url.length - 1)));
752
- UI.UIUtils.createTextChild(fragment, ',');
753
- } else {
754
- fragment.appendChild(linkifyValue(url));
755
- }
718
+ while (highlightIndex < highlightCount) {
719
+ result.entityRanges[highlightIndex].offset += additionalHighlightOffset;
720
+ ++highlightIndex;
721
+ }
722
+ element.setTextContentTruncatedIfNeeded(newValue);
723
+ Highlighting.highlightRangesWithStyleClass(element, result.entityRanges, 'webkit-html-entity-value');
724
+ }
725
+
726
+ function renderLinkifiedValue(value: string, node: SDK.DOMModel.DOMNode): Lit.TemplateResult {
727
+ const rewrittenHref = node ? node.resolveURL(value) : null;
728
+ if (rewrittenHref === null) {
729
+ return html`<span ${ref(el => {
730
+ if (el) {
731
+ setValueWithEntities(el, value);
756
732
  }
757
- if (descriptor) {
758
- UI.UIUtils.createTextChild(fragment, descriptor);
733
+ })}}></span>`;
734
+ }
735
+ value = value.replace(closingPunctuationRegex, '$&\u200B');
736
+ if (value.startsWith('data:')) {
737
+ value = Platform.StringUtilities.trimMiddle(value, 60);
738
+ }
739
+ const isAnchor = node && node.nodeName().toLowerCase() === 'a';
740
+ if (isAnchor) {
741
+ return html`<devtools-link class="devtools-link image-url" href=${rewrittenHref} ${ref(el => {
742
+ if (el) {
743
+ ImagePreviewPopover.setImageUrl(el, rewrittenHref);
759
744
  }
760
- value = value.substring(url.length + descriptor.length);
761
- }
762
- return fragment;
745
+ })}>${Platform.StringUtilities.trimMiddle(value, 150)}</devtools-link>`;
763
746
  }
764
-
765
- const nodeName = node ? node.nodeName().toLowerCase() : '';
766
- const setAttrValue = ref(el => {
767
- if (!el) {
768
- return;
769
- }
770
- const valueElement = el as HTMLElement;
771
- valueElement.removeChildren();
772
- if (nodeName && (name === 'src' || name === 'href') && value) {
773
- valueElement.appendChild(linkifyValue(value));
774
- } else if ((nodeName === 'img' || nodeName === 'source') && name === 'srcset') {
775
- valueElement.appendChild(linkifySrcset(value));
776
- } else if (nodeName === 'image' && (name === 'xlink:href' || name === 'href')) {
777
- valueElement.appendChild(linkifySrcset(value));
778
- } else {
779
- setValueWithEntities(valueElement, value);
747
+ return Components.Linkifier.Linkifier.renderLinkifiedUrl(rewrittenHref, {
748
+ text: value,
749
+ preventClick: true,
750
+ showColumnNumber: false,
751
+ inlineFrameIndex: 0,
752
+ onRef: link => {
753
+ ImagePreviewPopover.setImageUrl(link, rewrittenHref);
780
754
  }
781
755
  });
756
+ }
757
+
758
+ function renderAttribute(
759
+ attr: {name: string, value?: string}, updateRecord: Elements.ElementUpdateRecord.ElementUpdateRecord|null,
760
+ isDiff: boolean, node: SDK.DOMModel.DOMNode): Lit.LitTemplate {
761
+ const name = attr.name;
762
+ const value = attr.value || '';
763
+ const forceValue = isDiff;
764
+ const hasText = (forceValue || value.length > 0);
765
+ const jslog = VisualLogging.value(name === 'style' ? 'style-attribute' : 'attribute').track({
766
+ change: true,
767
+ dblclick: true,
768
+ });
782
769
 
783
770
  const relationRef =
784
771
  (relation: Protocol.DOM.GetElementByRelationRequestRelation, tooltip: string): ReturnType<typeof ref> =>
@@ -831,11 +818,41 @@ function renderAttribute(
831
818
  }
832
819
  }
833
820
 
834
- return html`<span class="webkit-html-attribute" jslog=${jslog}><span class="webkit-html-attribute-name" ${
835
- highlightNameAnimation} ${relationRefDirective}>${name}</span>${
836
- hasText ? html`=\u200B"<span class="webkit-html-attribute-value" ${highlightValueAnimation} ${setAttrValue} ${
837
- valueRelationRefDirective}></span>"` :
821
+ const nodeName = node ? node.nodeName().toLowerCase() : '';
822
+ const enum ValueType {
823
+ UNKNOWN = 0,
824
+ SRC = 1,
825
+ SRCSET = 2,
826
+ }
827
+ let valueType = ValueType.UNKNOWN;
828
+ if (nodeName && (name === 'src' || name === 'href') && value) {
829
+ valueType = ValueType.SRC;
830
+ } else if ((nodeName === 'img' || nodeName === 'source') && name === 'srcset') {
831
+ valueType = ValueType.SRCSET;
832
+ } else if (nodeName === 'image' && (name === 'xlink:href' || name === 'href')) {
833
+ valueType = ValueType.SRCSET;
834
+ }
835
+
836
+ const withEntitiesRef = valueType === ValueType.UNKNOWN ? ref(el => {
837
+ if (el) {
838
+ setValueWithEntities(el, value);
839
+ }
840
+ }) :
841
+ nothing;
842
+
843
+ // clang-format off
844
+ return html`<span class="webkit-html-attribute" jslog=${jslog}><span class="webkit-html-attribute-name"
845
+ ${animateOn(Boolean(updateRecord?.isAttributeModified(name) && !hasText), DOM_UPDATE_ANIMATION_CLASS_NAME)} ${
846
+ relationRefDirective}>${name}</span>${
847
+ hasText ? html`=\u200B"<span class="webkit-html-attribute-value" ${
848
+ animateOn(
849
+ Boolean(updateRecord?.isAttributeModified(name) && hasText),
850
+ DOM_UPDATE_ANIMATION_CLASS_NAME)} ${valueRelationRefDirective} ${withEntitiesRef}>
851
+ ${valueType === ValueType.SRC ? renderLinkifiedValue(value, node) : nothing}
852
+ ${valueType === ValueType.SRCSET ? renderLinkifiedSrcset(parseSrcset(value), node) : nothing}
853
+ </span>"` :
838
854
  nothing}</span>`;
855
+ // clang-format on
839
856
  }
840
857
 
841
858
  function renderTag(
@@ -855,13 +872,6 @@ function renderTag(
855
872
  hasUpdates = hasUpdates || (!expanded && updateRecord.hasChangedChildren());
856
873
  }
857
874
 
858
- const highlightAnimation = hasUpdates ? ref(el => {
859
- if (el) {
860
- UI.UIUtils.runCSSAnimationOnce(el, 'dom-update-highlight');
861
- }
862
- }) :
863
- nothing;
864
-
865
875
  // We are taking full text content of the tag, including attributes and children, to set the aria label.
866
876
  // FIXME: we should compute the aria label ourselves if it is event needed.
867
877
  const setAriaLabel = ref(el => {
@@ -876,7 +886,8 @@ function renderTag(
876
886
 
877
887
  return html`<span
878
888
  class=${Lit.Directives.classMap(classMap)} ${setAriaLabel}
879
- >&lt;<span class=${tagNameClass} jslog=${jslog || nothing} ${highlightAnimation}>${tagString}</span>${
889
+ >&lt;<span class=${tagNameClass} jslog=${jslog || nothing} ${
890
+ animateOn(hasUpdates, DOM_UPDATE_ANIMATION_CLASS_NAME)}>${tagString}</span>${
880
891
  attributes.map(attr => html` ${renderAttribute(attr, updateRecord, false, node)}`)}&gt;</span>\u200B`;
881
892
  }
882
893
 
@@ -1189,7 +1200,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1189
1200
 
1190
1201
  static animateOnDOMUpdate(treeElement: ElementsTreeElement): void {
1191
1202
  const tagName = treeElement.listItemElement.querySelector('.webkit-html-tag-name');
1192
- UI.UIUtils.runCSSAnimationOnce(tagName || treeElement.listItemElement, 'dom-update-highlight');
1203
+ UI.UIUtils.runCSSAnimationOnce(tagName || treeElement.listItemElement, DOM_UPDATE_ANIMATION_CLASS_NAME);
1193
1204
  }
1194
1205
 
1195
1206
  static visibleShadowRoots(node: SDK.DOMModel.DOMNode): SDK.DOMModel.DOMNode[] {
@@ -1420,7 +1431,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1420
1431
  }
1421
1432
  }
1422
1433
  }
1423
- UI.UIUtils.runCSSAnimationOnce(animationElement, 'dom-update-highlight');
1434
+ UI.UIUtils.runCSSAnimationOnce(animationElement, DOM_UPDATE_ANIMATION_CLASS_NAME);
1424
1435
  }
1425
1436
 
1426
1437
  isClosingTag(): boolean {
@@ -1726,13 +1737,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1726
1737
  if (this.editing) {
1727
1738
  return false;
1728
1739
  }
1729
- const handledByFloaty = UI.Floaty.onFloatyClick({
1730
- type: UI.Floaty.FloatyContextTypes.ELEMENT_NODE_ID,
1731
- data: {nodeId: this.nodeInternal.id},
1732
- });
1733
- if (handledByFloaty) {
1734
- return false;
1735
- }
1736
1740
  return super.select(omitFocus, selectedByUser);
1737
1741
  }
1738
1742
 
@@ -22,22 +22,20 @@ const str_ = i18n.i18n.registerUIStrings('panels/elements/NodeStackTraceWidget.t
22
22
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
23
23
 
24
24
  interface ViewInput {
25
- target?: SDK.Target.Target;
26
- linkifier: Components.Linkifier.Linkifier;
27
25
  stackTrace?: StackTrace.StackTrace.StackTrace;
28
26
  }
29
27
 
30
28
  type View = (input: ViewInput, output: object, target: HTMLElement) => void;
31
29
 
32
30
  export const DEFAULT_VIEW: View = (input, _output, target) => {
33
- const {target: sdkTarget, linkifier, stackTrace} = input;
31
+ const {stackTrace} = input;
34
32
  // clang-format off
35
33
  render(html`
36
34
  <style>${nodeStackTraceWidgetStyles}</style>
37
35
  ${target && stackTrace ?
38
36
  html`<devtools-widget
39
37
  class="stack-trace"
40
- .widgetConfig=${UI.Widget.widgetConfig(Components.JSPresentationUtils.StackTracePreviewContent, {target: sdkTarget, linkifier, stackTrace})}>
38
+ .widgetConfig=${UI.Widget.widgetConfig(Components.JSPresentationUtils.StackTracePreviewContent, {stackTrace})}>
41
39
  </devtools-widget>` :
42
40
  html`<div class="gray-info-message">${i18nString(UIStrings.noStackTraceAvailable)}</div>`}`,
43
41
  target);
@@ -45,7 +43,6 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
45
43
  };
46
44
 
47
45
  export class NodeStackTraceWidget extends UI.Widget.VBox {
48
- readonly #linkifier = new Components.Linkifier.Linkifier(MaxLengthForLinks);
49
46
  readonly #view: View;
50
47
 
51
48
  constructor(view = DEFAULT_VIEW) {
@@ -73,13 +70,6 @@ export class NodeStackTraceWidget extends UI.Widget.VBox {
73
70
  await Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().createStackTraceFromProtocolRuntime(
74
71
  runtimeStackTrace, target) :
75
72
  undefined;
76
- const input: ViewInput = {
77
- target,
78
- linkifier: this.#linkifier,
79
- stackTrace,
80
- };
81
- this.#view(input, {}, this.contentElement);
73
+ this.#view({stackTrace}, {}, this.contentElement);
82
74
  }
83
75
  }
84
-
85
- export const MaxLengthForLinks = 40;
@@ -0,0 +1,69 @@
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 Common from '../../core/common/common.js';
6
+ import * as Host from '../../core/host/host.js';
7
+ import * as i18n from '../../core/i18n/i18n.js';
8
+ import * as AiCodeCompletion from '../../models/ai_code_completion/ai_code_completion.js';
9
+ import type * as TextEditor from '../../ui/components/text_editor/text_editor.js';
10
+
11
+ export class StylesAiCodeCompletionProvider {
12
+ #aidaClient: Host.AidaClient.AidaClient = new Host.AidaClient.AidaClient();
13
+ #aiCodeCompletionSetting = Common.Settings.Settings.instance().createSetting('ai-code-completion-enabled', false);
14
+ #aiCodeCompletion?: AiCodeCompletion.AiCodeCompletion.AiCodeCompletion;
15
+ #aiCodeCompletionConfig?: TextEditor.AiCodeCompletionProvider.AiCodeCompletionConfig;
16
+
17
+ #boundOnUpdateAiCodeCompletionState = this.#updateAiCodeCompletionState.bind(this);
18
+
19
+ private constructor(aiCodeCompletionConfig: TextEditor.AiCodeCompletionProvider.AiCodeCompletionConfig) {
20
+ const devtoolsLocale = i18n.DevToolsLocale.DevToolsLocale.instance();
21
+ if (!AiCodeCompletion.AiCodeCompletion.AiCodeCompletion.isAiCodeCompletionStylesEnabled(devtoolsLocale.locale)) {
22
+ throw new Error('AI code completion feature in Styles is not enabled.');
23
+ }
24
+ this.#aiCodeCompletionConfig = aiCodeCompletionConfig;
25
+ Host.AidaClient.HostConfigTracker.instance().addEventListener(
26
+ Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnUpdateAiCodeCompletionState);
27
+ this.#aiCodeCompletionSetting.addChangeListener(this.#boundOnUpdateAiCodeCompletionState);
28
+ void this.#updateAiCodeCompletionState();
29
+ }
30
+
31
+ static createInstance(aiCodeCompletionConfig: TextEditor.AiCodeCompletionProvider.AiCodeCompletionConfig):
32
+ StylesAiCodeCompletionProvider {
33
+ return new StylesAiCodeCompletionProvider(aiCodeCompletionConfig);
34
+ }
35
+
36
+ #setupAiCodeCompletion(): void {
37
+ if (!this.#aiCodeCompletionConfig) {
38
+ return;
39
+ }
40
+ if (this.#aiCodeCompletion) {
41
+ // early return as this means that code completion was previously setup
42
+ return;
43
+ }
44
+ this.#aiCodeCompletion = new AiCodeCompletion.AiCodeCompletion.AiCodeCompletion(
45
+ {aidaClient: this.#aidaClient}, this.#aiCodeCompletionConfig.panel, undefined,
46
+ this.#aiCodeCompletionConfig.completionContext.stopSequences);
47
+ this.#aiCodeCompletionConfig.onFeatureEnabled();
48
+ }
49
+
50
+ #cleanupAiCodeCompletion(): void {
51
+ if (!this.#aiCodeCompletion) {
52
+ // early return as this means there is no code completion to clean up
53
+ return;
54
+ }
55
+ this.#aiCodeCompletion = undefined;
56
+ this.#aiCodeCompletionConfig?.onFeatureDisabled();
57
+ }
58
+
59
+ async #updateAiCodeCompletionState(): Promise<void> {
60
+ const aidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
61
+ const isAvailable = aidaAvailability === Host.AidaClient.AidaAccessPreconditions.AVAILABLE;
62
+ const isEnabled = this.#aiCodeCompletionSetting.get();
63
+ if (isAvailable && isEnabled) {
64
+ this.#setupAiCodeCompletion();
65
+ } else {
66
+ this.#cleanupAiCodeCompletion();
67
+ }
68
+ }
69
+ }
@@ -14,12 +14,7 @@
14
14
  width: 100%;
15
15
  }
16
16
 
17
- .styles-sidebar-computed-style-widget {
18
- min-height: auto;
19
- }
20
-
21
- .computed-style-tree-outline-container {
22
- flex-grow: 1;
17
+ devtools-tree-outline {
23
18
  flex-shrink: 0;
24
19
  }
25
20
 
@@ -4,7 +4,6 @@
4
4
 
5
5
  import './InspectElementModeController.js';
6
6
  import './ColorSwatchPopoverIcon.js';
7
-
8
7
  import './DOMPath.js';
9
8
  import './ElementsSidebarPane.js';
10
9
  import './ElementsTreeElement.js';
@@ -20,6 +19,7 @@ import './NodeStackTraceWidget.js';
20
19
  import './StylePropertiesSection.js';
21
20
  import './StylePropertyHighlighter.js';
22
21
  import './StylesSidebarPane.js';
22
+ import './StylesAiCodeCompletionProvider.js';
23
23
  import './StylePropertyTreeElement.js';
24
24
  import './ComputedStyleWidget.js';
25
25
  import './CSSRuleValidator.js';
@@ -59,6 +59,7 @@ import * as StylePropertiesSection from './StylePropertiesSection.js';
59
59
  import * as StylePropertyHighlighter from './StylePropertyHighlighter.js';
60
60
  import * as StylePropertyTreeElement from './StylePropertyTreeElement.js';
61
61
  import * as StylePropertyUtils from './StylePropertyUtils.js';
62
+ import * as StylesAiCodeCompletionProvider from './StylesAiCodeCompletionProvider.js';
62
63
  import * as StylesSidebarPane from './StylesSidebarPane.js';
63
64
  import * as TopLayerContainer from './TopLayerContainer.js';
64
65
  import * as WebCustomData from './WebCustomData.js';
@@ -94,6 +95,7 @@ export {
94
95
  StylePropertyHighlighter,
95
96
  StylePropertyTreeElement,
96
97
  StylePropertyUtils,
98
+ StylesAiCodeCompletionProvider,
97
99
  StylesSidebarPane,
98
100
  TopLayerContainer,
99
101
  WebCustomData,
@@ -253,8 +253,7 @@ export abstract class AffectedResourcesView extends UI.TreeOutline.TreeElement {
253
253
  const sourceCodeLocation = document.createElement('td');
254
254
  sourceCodeLocation.classList.add('affected-source-location');
255
255
  if (sourceLocation) {
256
- const maxLengthForDisplayedURLs = 40; // Same as console messages.
257
- const linkifier = new Components.Linkifier.Linkifier(maxLengthForDisplayedURLs);
256
+ const linkifier = new Components.Linkifier.Linkifier(UI.UIUtils.MaxLengthForDisplayedURLsInConsole);
258
257
  const sourceAnchor = linkifier.linkifyScriptLocation(
259
258
  target || null, sourceLocation.scriptId || null, sourceLocation.url as Platform.DevToolsPath.UrlString,
260
259
  sourceLocation.lineNumber, {columnNumber: sourceLocation.columnNumber, inlineFrameIndex: 0});
@@ -1185,14 +1185,6 @@ export class NetworkRequestNode extends NetworkNode {
1185
1185
  }
1186
1186
 
1187
1187
  override select(suppressSelectedEvent?: boolean): void {
1188
- const id = this.request()?.requestId();
1189
- if (id) {
1190
- const floatyHandled =
1191
- UI.Floaty.onFloatyClick({type: UI.Floaty.FloatyContextTypes.NETWORK_REQUEST, data: {requestId: id}});
1192
- if (floatyHandled) {
1193
- return;
1194
- }
1195
- }
1196
1188
  super.select(suppressSelectedEvent);
1197
1189
  this.parentView().dispatchEventToListeners(Events.RequestSelected, this.requestInternal);
1198
1190
  }