chrome-devtools-frontend 1.0.1585538 → 1.0.1586699

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 (51) hide show
  1. package/front_end/core/common/Srcset.ts +61 -0
  2. package/front_end/core/common/common.ts +2 -0
  3. package/front_end/generated/SupportedCSSProperties.js +4 -2
  4. package/front_end/models/ai_assistance/AiConversation.ts +6 -2
  5. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +12 -0
  6. package/front_end/models/javascript_metadata/NativeFunctions.js +2 -2
  7. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +1 -0
  8. package/front_end/panels/ai_assistance/components/ChatView.ts +13 -0
  9. package/front_end/panels/application/FrameDetailsView.ts +4 -9
  10. package/front_end/panels/console/ConsoleViewMessage.ts +2 -2
  11. package/front_end/panels/console/PromptBuilder.ts +15 -3
  12. package/front_end/panels/elements/ElementsTreeElement.ts +95 -106
  13. package/front_end/panels/elements/NodeStackTraceWidget.ts +3 -11
  14. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +69 -0
  15. package/front_end/panels/elements/elements.ts +3 -1
  16. package/front_end/panels/network/RequestInitiatorView.ts +2 -10
  17. package/front_end/panels/network/components/RequestHeadersView.ts +85 -153
  18. package/front_end/panels/sources/CallStackSidebarPane.ts +3 -7
  19. package/front_end/panels/timeline/TimelineUIUtils.ts +2 -6
  20. package/front_end/panels/utils/utils.ts +13 -5
  21. package/front_end/third_party/chromium/README.chromium +1 -1
  22. package/front_end/third_party/puppeteer/README.chromium +2 -2
  23. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/FrameManager.js +1 -1
  24. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/FrameManager.js.map +1 -1
  25. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/util.d.ts.map +1 -1
  26. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/util.js +7 -7
  27. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/util.js.map +1 -1
  28. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  29. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  30. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  31. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  32. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +7 -7
  33. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/FrameManager.js +1 -1
  34. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/FrameManager.js.map +1 -1
  35. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/util.d.ts.map +1 -1
  36. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/util.js +7 -7
  37. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/util.js.map +1 -1
  38. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  39. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  40. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  41. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  42. package/front_end/third_party/puppeteer/package/package.json +2 -2
  43. package/front_end/third_party/puppeteer/package/src/cdp/FrameManager.ts +1 -1
  44. package/front_end/third_party/puppeteer/package/src/common/util.ts +9 -8
  45. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  46. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  47. package/front_end/ui/legacy/UIUtils.ts +31 -14
  48. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +96 -216
  49. package/front_end/ui/legacy/components/utils/Linkifier.ts +7 -3
  50. package/front_end/ui/legacy/components/utils/jsUtils.css +0 -9
  51. package/package.json +1 -1
@@ -0,0 +1,61 @@
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
+ export const enum TokenType {
6
+ LITERAL = 0,
7
+ URL = 1
8
+ }
9
+
10
+ export interface Token {
11
+ type: TokenType;
12
+ value: string;
13
+ }
14
+
15
+ /**
16
+ * Parsing of
17
+ * https://html.spec.whatwg.org/multipage/images.html#srcset-attribute and href
18
+ * attributes to identify URLs vs other text in the srcset.
19
+ *
20
+ * Note: this is probably not spec compliant.
21
+ */
22
+ export function parseSrcset(value: string): Token[] {
23
+ const result: Token[] = [];
24
+ let i = 0;
25
+ while (value.length) {
26
+ if (i++ > 0) {
27
+ result.push({value: ' ', type: TokenType.LITERAL});
28
+ }
29
+ value = value.trim();
30
+ let url = '';
31
+ let descriptor = '';
32
+ const indexOfSpace = value.search(/\s/);
33
+ if (indexOfSpace === -1) {
34
+ url = value;
35
+ } else if (indexOfSpace > 0 && value[indexOfSpace - 1] === ',') {
36
+ url = value.substring(0, indexOfSpace);
37
+ } else {
38
+ url = value.substring(0, indexOfSpace);
39
+ const indexOfComma = value.indexOf(',', indexOfSpace);
40
+ if (indexOfComma !== -1) {
41
+ descriptor = value.substring(indexOfSpace, indexOfComma + 1);
42
+ } else {
43
+ descriptor = value.substring(indexOfSpace);
44
+ }
45
+ }
46
+
47
+ if (url) {
48
+ if (url.endsWith(',')) {
49
+ result.push({value: url.substring(0, url.length - 1), type: TokenType.URL});
50
+ result.push({type: TokenType.LITERAL, value: ','});
51
+ } else {
52
+ result.push({value: url, type: TokenType.URL});
53
+ }
54
+ }
55
+ if (descriptor) {
56
+ result.push({type: TokenType.LITERAL, value: descriptor});
57
+ }
58
+ value = value.substring(url.length + descriptor.length);
59
+ }
60
+ return result;
61
+ }
@@ -29,6 +29,7 @@ import * as SegmentedRange from './SegmentedRange.js';
29
29
  import * as SettingRegistration from './SettingRegistration.js';
30
30
  import * as Settings from './Settings.js';
31
31
  import * as SimpleHistoryManager from './SimpleHistoryManager.js';
32
+ import * as Srcset from './Srcset.js';
32
33
  import * as StringOutputStream from './StringOutputStream.js';
33
34
  import * as TextDictionary from './TextDictionary.js';
34
35
  import * as Throttler from './Throttler.js';
@@ -68,6 +69,7 @@ export {
68
69
  SettingRegistration,
69
70
  Settings,
70
71
  SimpleHistoryManager,
72
+ Srcset,
71
73
  StringOutputStream,
72
74
  TextDictionary,
73
75
  Throttler,
@@ -4426,7 +4426,8 @@ export const generatedProperties = [
4426
4426
  "inherited": true,
4427
4427
  "keywords": [
4428
4428
  "none",
4429
- "auto"
4429
+ "auto",
4430
+ "all"
4430
4431
  ],
4431
4432
  "name": "text-decoration-skip-ink"
4432
4433
  },
@@ -6976,7 +6977,8 @@ export const generatedPropertyValues = {
6976
6977
  "text-decoration-skip-ink": {
6977
6978
  "values": [
6978
6979
  "none",
6979
- "auto"
6980
+ "auto",
6981
+ "all"
6980
6982
  ]
6981
6983
  },
6982
6984
  "text-decoration-style": {
@@ -456,16 +456,20 @@ Time: ${micros(time)}`;
456
456
  },
457
457
  options.multimodalInput,
458
458
  )) {
459
+ // Add to history if relevant
459
460
  if (shouldAddToHistory(data)) {
460
461
  void this.addHistoryItem(data);
461
462
  }
463
+ // Always yield the data
464
+ yield data;
465
+
466
+ // If we change the context
467
+ // requery with the specialized agent.
462
468
  if (data.type === ResponseType.CONTEXT_CHANGE) {
463
469
  this.setContext(data.context);
464
470
  yield* this.#runAgent(initialQuery, options);
465
471
  return;
466
472
  }
467
-
468
- yield data;
469
473
  }
470
474
  }
471
475
 
@@ -346,6 +346,18 @@ export class AiCodeCompletion {
346
346
  }
347
347
  return Boolean(aidaAvailability.enabled && Root.Runtime.hostConfig.devToolsAiCodeCompletion?.enabled);
348
348
  }
349
+
350
+ static isAiCodeCompletionStylesEnabled(locale: string): boolean {
351
+ if (!locale.startsWith('en-')) {
352
+ return false;
353
+ }
354
+ const aidaAvailability = Root.Runtime.hostConfig.aidaAvailability;
355
+ if (!aidaAvailability || aidaAvailability.blockedByGeo || aidaAvailability.blockedByAge ||
356
+ aidaAvailability.blockedByEnterprisePolicy) {
357
+ return false;
358
+ }
359
+ return Boolean(aidaAvailability.enabled && Root.Runtime.hostConfig.devToolsAiCodeCompletionStyles?.enabled);
360
+ }
349
361
  }
350
362
 
351
363
  export const enum ContextFlavor {
@@ -6624,8 +6624,8 @@ export const NativeFunctions = [
6624
6624
  signatures: [["type","?eventInitDict"]]
6625
6625
  },
6626
6626
  {
6627
- name: "setFormControlRange",
6628
- signatures: [["element","start","end"]]
6627
+ name: "getValueRange",
6628
+ signatures: [["start","end"]]
6629
6629
  },
6630
6630
  {
6631
6631
  name: "getBoxQuads",
@@ -1621,6 +1621,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1621
1621
  this.#handleConversationContextChange(data.context);
1622
1622
  step.isLoading = false;
1623
1623
  commitStep();
1624
+ step = {isLoading: true};
1624
1625
 
1625
1626
  break;
1626
1627
  }
@@ -183,6 +183,19 @@ const DEFAULT_VIEW: View = (input, output, target) => {
183
183
  `, target);
184
184
  // clang-format on
185
185
  };
186
+
187
+ /**
188
+ * ChatView is a web component for historical reasons and generally should not
189
+ * exist because it barely has any presenter logic and it is definitely not
190
+ * re-usable as a custom element. Instead, the template from ChatView should be
191
+ * embdedded into the AiAssistancePanel (the sole host of chat interfaces) and
192
+ * the scroll handling logic should be implemented in view functions using refs
193
+ * or re-usable custom elements. Currently, the ChatView just combines the
194
+ * interfaces of ChatMessage and ChatInput presenters and passes most of the
195
+ * properties down to those presenters as-is.
196
+ *
197
+ * @deprecated
198
+ */
186
199
  export class ChatView extends HTMLElement {
187
200
  readonly #shadow = this.attachShadow({mode: 'open'});
188
201
  #scrollTop?: number;
@@ -272,7 +272,6 @@ interface FrameDetailsViewInput {
272
272
  frame: SDK.ResourceTreeModel.ResourceTreeFrame;
273
273
  target: SDK.Target.Target|null;
274
274
  creationStackTrace: StackTrace.StackTrace.StackTrace|null;
275
- creationTarget: SDK.Target.Target|null;
276
275
  adScriptAncestry: Protocol.Page.AdScriptAncestry|null;
277
276
  linkTargetDOMNode: SDK.DOMModel.DOMNode|null;
278
277
  permissionsPolicies: Protocol.Page.PermissionsPolicyFeatureState[]|null;
@@ -354,7 +353,7 @@ function renderDocumentSection(input: FrameDetailsViewInput): LitTemplate {
354
353
  ${maybeRenderUnreachableURL(input.frame?.unreachableUrl())}
355
354
  ${maybeRenderOrigin(input.frame?.securityOrigin)}
356
355
  ${renderOwnerElement(input.linkTargetDOMNode)}
357
- ${maybeRenderCreationStacktrace(input.creationStackTrace, input.creationTarget)}
356
+ ${maybeRenderCreationStacktrace(input.creationStackTrace)}
358
357
  ${maybeRenderAdStatus(input.frame?.adFrameType(), input.frame?.adFrameStatus())}
359
358
  ${maybeRenderCreatorAdScriptAncestry(input.frame?.adFrameType(), input.target, input.adScriptAncestry)}
360
359
  <devtools-report-divider></devtools-report-divider>`;
@@ -447,9 +446,8 @@ function renderOwnerElement(linkTargetDOMNode: SDK.DOMModel.DOMNode|null): LitTe
447
446
  return nothing;
448
447
  }
449
448
 
450
- function maybeRenderCreationStacktrace(
451
- stackTrace: StackTrace.StackTrace.StackTrace|null, target: SDK.Target.Target|null): LitTemplate {
452
- if (stackTrace && target) {
449
+ function maybeRenderCreationStacktrace(stackTrace: StackTrace.StackTrace.StackTrace|null): LitTemplate {
450
+ if (stackTrace) {
453
451
  // Disabled until https://crbug.com/1079231 is fixed.
454
452
  // clang-format off
455
453
  return html`
@@ -457,7 +455,7 @@ function maybeRenderCreationStacktrace(
457
455
  i18nString(UIStrings.creationStackTrace)}</devtools-report-key>
458
456
  <devtools-report-value jslog=${VisualLogging.section('frame-creation-stack-trace')}>
459
457
  <devtools-widget .widgetConfig=${UI.Widget.widgetConfig(
460
- Components.JSPresentationUtils.StackTracePreviewContent, {target, stackTrace, options: {expandable: true}})}>
458
+ Components.JSPresentationUtils.StackTracePreviewContent, {stackTrace, options: {expandable: true}})}>
461
459
  </devtools-widget>
462
460
  </devtools-report-value>
463
461
  `;
@@ -844,7 +842,6 @@ export class FrameDetailsReportView extends UI.Widget.Widget {
844
842
  #frame?: SDK.ResourceTreeModel.ResourceTreeFrame;
845
843
  #target: SDK.Target.Target|null = null;
846
844
  #creationStackTrace: StackTrace.StackTrace.StackTrace|null = null;
847
- #creationTarget: SDK.Target.Target|null = null;
848
845
  #securityIsolationInfo: Protocol.Network.SecurityIsolationStatus|null = null;
849
846
  #linkTargetDOMNode: SDK.DOMModel.DOMNode|null = null;
850
847
  #trials: Protocol.Page.OriginTrial[]|null = null;
@@ -869,7 +866,6 @@ export class FrameDetailsReportView extends UI.Widget.Widget {
869
866
  });
870
867
  const {creationStackTrace: rawCreationStackTrace, creationStackTraceTarget: creationTarget} =
871
868
  frame.getCreationStackTraceData();
872
- this.#creationTarget = creationTarget;
873
869
  if (rawCreationStackTrace) {
874
870
  void Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()
875
871
  .createStackTraceFromProtocolRuntime(rawCreationStackTrace, creationTarget)
@@ -924,7 +920,6 @@ export class FrameDetailsReportView extends UI.Widget.Widget {
924
920
  frame,
925
921
  target: this.#target,
926
922
  creationStackTrace: this.#creationStackTrace,
927
- creationTarget: this.#creationTarget,
928
923
  protocolMonitorExperimentEnabled: this.#protocolMonitorExperimentEnabled,
929
924
  permissionsPolicies: this.#permissionsPolicies,
930
925
  adScriptAncestry: this.#adScriptAncestry,
@@ -705,8 +705,8 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
705
705
  const stackTraceElement = contentElement.createChild('div', 'hidden-stack-trace');
706
706
  const targetManager = SDK.TargetManager.TargetManager.instance();
707
707
  const stackTraceTarget = target ?? targetManager.primaryPageTarget() ?? targetManager.rootTarget();
708
- const stackTracePreview = new Components.JSPresentationUtils.StackTracePreviewContent(
709
- undefined, stackTraceTarget ?? undefined, this.linkifier, {widthConstrained: true});
708
+ const stackTracePreview = new Components.JSPresentationUtils.StackTracePreviewContent();
709
+ stackTracePreview.options = {widthConstrained: true};
710
710
  if (stackTraceTarget && stackTrace) {
711
711
  const selectableChildIndex = this.selectableChildren.length;
712
712
  void Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()
@@ -9,6 +9,7 @@ import * as Formatter from '../../models/formatter/formatter.js';
9
9
  import * as Logs from '../../models/logs/logs.js';
10
10
  import * as TextUtils from '../../models/text_utils/text_utils.js';
11
11
  import * as Components from '../../ui/legacy/components/utils/utils.js';
12
+ import * as UI from '../../ui/legacy/legacy.js';
12
13
 
13
14
  import type {ConsoleViewMessage} from './ConsoleViewMessage.js';
14
15
 
@@ -85,7 +86,7 @@ export class PromptBuilder {
85
86
 
86
87
  const relatedCode = sourceCode?.text ? formatRelatedCode(sourceCode) : '';
87
88
  const relatedRequest = request ? formatNetworkRequest(request) : '';
88
- const stacktrace = sourcesTypes.includes(SourceType.STACKTRACE) ? formatStackTrace(this.#consoleMessage) : '';
89
+ const stacktrace = sourcesTypes.includes(SourceType.STACKTRACE) ? await formatStackTrace(this.#consoleMessage) : '';
89
90
 
90
91
  const message = formatConsoleMessage(this.#consoleMessage);
91
92
 
@@ -290,14 +291,25 @@ export function formatConsoleMessage(message: ConsoleViewMessage): string {
290
291
  * This formats the stacktrace from the console message which might or might not
291
292
  * match the content of stacktrace(s) in the console message arguments.
292
293
  */
293
- export function formatStackTrace(message: ConsoleViewMessage): string {
294
+ async function formatStackTrace(message: ConsoleViewMessage): Promise<string> {
294
295
  const previewContainer = message.contentElement().querySelector('.stack-preview-container');
295
296
 
296
297
  if (!previewContainer) {
297
298
  return '';
298
299
  }
299
300
 
300
- const preview = previewContainer.shadowRoot?.querySelector('.stack-preview-container') as HTMLElement;
301
+ const widget =
302
+ UI.Widget.Widget.get(previewContainer) as Components.JSPresentationUtils.StackTracePreviewContent | undefined;
303
+ if (!widget) {
304
+ return '';
305
+ }
306
+
307
+ await widget.updateComplete;
308
+
309
+ // TODO(crbug.com/433162438): Get the `StackTrace` from the widget and render that instead
310
+ // of crawling the DOM. `StackTrace` is source mapped and has ignore listing.
311
+
312
+ const preview = widget.contentElement.querySelector('.stack-preview-container') as HTMLElement;
301
313
 
302
314
  const nodes = preview.childTextNodes();
303
315
  // Gets application-level source mapped stack trace taking the ignore list
@@ -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,7 @@ 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
76
  const {animateOn} = UI.UIUtils;
77
77
 
78
78
  const UIStrings = {
@@ -634,124 +634,86 @@ function renderTitle(
634
634
  }
635
635
  }
636
636
 
637
- function renderAttribute(
638
- attr: {name: string, value?: string}, updateRecord: Elements.ElementUpdateRecord.ElementUpdateRecord|null,
639
- isDiff: boolean, node: SDK.DOMModel.DOMNode): Lit.LitTemplate {
640
- const name = attr.name;
641
- const value = attr.value || '';
642
- const forceValue = isDiff;
643
- const closingPunctuationRegex = /[\/;:\)\]\}]/g;
637
+ function renderLinkifiedSrcset(tokens: Common.Srcset.Token[], node: SDK.DOMModel.DOMNode): Lit.TemplateResult {
638
+ return html`${repeat(tokens, token => {
639
+ switch (token.type) {
640
+ case Common.Srcset.TokenType.URL:
641
+ return renderLinkifiedValue(token.value, node);
642
+ case Common.Srcset.TokenType.LITERAL:
643
+ return token.value;
644
+ }
645
+ })}`;
646
+ }
647
+
648
+ const closingPunctuationRegex = /[\/;:\)\]\}]/g;
649
+
650
+ // FIXME: this should be made declarative next.
651
+ function setValueWithEntities(element: Element, value: string): void {
644
652
  let highlightIndex = 0;
645
653
  let highlightCount = 0;
646
654
  let additionalHighlightOffset = 0;
647
-
648
- function setValueWithEntities(element: Element, value: string): void {
649
- const result = convertUnicodeCharsToHTMLEntities(value);
650
- highlightCount = result.entityRanges.length;
651
- const newValue = result.text.replace(closingPunctuationRegex, (match, replaceOffset) => {
652
- while (highlightIndex < highlightCount && result.entityRanges[highlightIndex].offset < replaceOffset) {
653
- result.entityRanges[highlightIndex].offset += additionalHighlightOffset;
654
- ++highlightIndex;
655
- }
656
- additionalHighlightOffset += 1;
657
- return match + '\u200B';
658
- });
659
-
660
- while (highlightIndex < highlightCount) {
655
+ const result = convertUnicodeCharsToHTMLEntities(value);
656
+ highlightCount = result.entityRanges.length;
657
+ const newValue = result.text.replace(closingPunctuationRegex, (match, replaceOffset) => {
658
+ while (highlightIndex < highlightCount && result.entityRanges[highlightIndex].offset < replaceOffset) {
661
659
  result.entityRanges[highlightIndex].offset += additionalHighlightOffset;
662
660
  ++highlightIndex;
663
661
  }
664
- element.setTextContentTruncatedIfNeeded(newValue);
665
- Highlighting.highlightRangesWithStyleClass(element, result.entityRanges, 'webkit-html-entity-value');
666
- }
667
-
668
- const hasText = (forceValue || value.length > 0);
669
- const jslog = VisualLogging.value(name === 'style' ? 'style-attribute' : 'attribute').track({
670
- change: true,
671
- dblclick: true,
662
+ additionalHighlightOffset += 1;
663
+ return match + '\u200B';
672
664
  });
673
665
 
674
- function linkifyValue(value: string): Element {
675
- const rewrittenHref = node ? node.resolveURL(value) : null;
676
- if (rewrittenHref === null) {
677
- const span = document.createElement('span');
678
- setValueWithEntities(span, value);
679
- return span;
680
- }
681
- value = value.replace(closingPunctuationRegex, '$&\u200B');
682
- if (value.startsWith('data:')) {
683
- value = Platform.StringUtilities.trimMiddle(value, 60);
684
- }
685
- const link = node && node.nodeName().toLowerCase() === 'a' ?
686
- Link.create(rewrittenHref, value, undefined, 'image-url') :
687
- Components.Linkifier.Linkifier.linkifyURL(rewrittenHref, {
688
- text: value,
689
- preventClick: true,
690
- showColumnNumber: false,
691
- inlineFrameIndex: 0,
692
- });
693
- return ImagePreviewPopover.setImageUrl(link, rewrittenHref);
666
+ while (highlightIndex < highlightCount) {
667
+ result.entityRanges[highlightIndex].offset += additionalHighlightOffset;
668
+ ++highlightIndex;
694
669
  }
670
+ element.setTextContentTruncatedIfNeeded(newValue);
671
+ Highlighting.highlightRangesWithStyleClass(element, result.entityRanges, 'webkit-html-entity-value');
672
+ }
695
673
 
696
- function linkifySrcset(value: string): DocumentFragment {
697
- // Splitting normally on commas or spaces will break on valid srcsets "foo 1x,bar 2x" and "data:,foo 1x".
698
- const fragment = document.createDocumentFragment();
699
- let i = 0;
700
- while (value.length) {
701
- if (i++ > 0) {
702
- UI.UIUtils.createTextChild(fragment, ' ');
703
- }
704
- value = value.trim();
705
- let url = '';
706
- let descriptor = '';
707
- const indexOfSpace = value.search(/\s/);
708
- if (indexOfSpace === -1) {
709
- url = value;
710
- } else if (indexOfSpace > 0 && value[indexOfSpace - 1] === ',') {
711
- url = value.substring(0, indexOfSpace);
712
- } else {
713
- url = value.substring(0, indexOfSpace);
714
- const indexOfComma = value.indexOf(',', indexOfSpace);
715
- if (indexOfComma !== -1) {
716
- descriptor = value.substring(indexOfSpace, indexOfComma + 1);
717
- } else {
718
- descriptor = value.substring(indexOfSpace);
719
- }
720
- }
721
-
722
- if (url) {
723
- if (url.endsWith(',')) {
724
- fragment.appendChild(linkifyValue(url.substring(0, url.length - 1)));
725
- UI.UIUtils.createTextChild(fragment, ',');
726
- } else {
727
- fragment.appendChild(linkifyValue(url));
728
- }
674
+ function renderLinkifiedValue(value: string, node: SDK.DOMModel.DOMNode): Lit.TemplateResult {
675
+ const rewrittenHref = node ? node.resolveURL(value) : null;
676
+ if (rewrittenHref === null) {
677
+ return html`<span ${ref(el => {
678
+ if (el) {
679
+ setValueWithEntities(el, value);
729
680
  }
730
- if (descriptor) {
731
- UI.UIUtils.createTextChild(fragment, descriptor);
681
+ })}}></span>`;
682
+ }
683
+ value = value.replace(closingPunctuationRegex, '$&\u200B');
684
+ if (value.startsWith('data:')) {
685
+ value = Platform.StringUtilities.trimMiddle(value, 60);
686
+ }
687
+ const isAnchor = node && node.nodeName().toLowerCase() === 'a';
688
+ if (isAnchor) {
689
+ return html`<devtools-link class="devtools-link image-url" href=${rewrittenHref} ${ref(el => {
690
+ if (el) {
691
+ ImagePreviewPopover.setImageUrl(el, rewrittenHref);
732
692
  }
733
- value = value.substring(url.length + descriptor.length);
734
- }
735
- return fragment;
693
+ })}>${Platform.StringUtilities.trimMiddle(value, 150)}</devtools-link>`;
736
694
  }
737
-
738
- const nodeName = node ? node.nodeName().toLowerCase() : '';
739
- const setAttrValue = ref(el => {
740
- if (!el) {
741
- return;
742
- }
743
- const valueElement = el as HTMLElement;
744
- valueElement.removeChildren();
745
- if (nodeName && (name === 'src' || name === 'href') && value) {
746
- valueElement.appendChild(linkifyValue(value));
747
- } else if ((nodeName === 'img' || nodeName === 'source') && name === 'srcset') {
748
- valueElement.appendChild(linkifySrcset(value));
749
- } else if (nodeName === 'image' && (name === 'xlink:href' || name === 'href')) {
750
- valueElement.appendChild(linkifySrcset(value));
751
- } else {
752
- setValueWithEntities(valueElement, value);
695
+ return Components.Linkifier.Linkifier.renderLinkifiedUrl(rewrittenHref, {
696
+ text: value,
697
+ preventClick: true,
698
+ showColumnNumber: false,
699
+ inlineFrameIndex: 0,
700
+ onRef: link => {
701
+ ImagePreviewPopover.setImageUrl(link, rewrittenHref);
753
702
  }
754
703
  });
704
+ }
705
+
706
+ function renderAttribute(
707
+ attr: {name: string, value?: string}, updateRecord: Elements.ElementUpdateRecord.ElementUpdateRecord|null,
708
+ isDiff: boolean, node: SDK.DOMModel.DOMNode): Lit.LitTemplate {
709
+ const name = attr.name;
710
+ const value = attr.value || '';
711
+ const forceValue = isDiff;
712
+ const hasText = (forceValue || value.length > 0);
713
+ const jslog = VisualLogging.value(name === 'style' ? 'style-attribute' : 'attribute').track({
714
+ change: true,
715
+ dblclick: true,
716
+ });
755
717
 
756
718
  const relationRef =
757
719
  (relation: Protocol.DOM.GetElementByRelationRequestRelation, tooltip: string): ReturnType<typeof ref> =>
@@ -804,14 +766,41 @@ function renderAttribute(
804
766
  }
805
767
  }
806
768
 
769
+ const nodeName = node ? node.nodeName().toLowerCase() : '';
770
+ const enum ValueType {
771
+ UNKNOWN = 0,
772
+ SRC = 1,
773
+ SRCSET = 2,
774
+ }
775
+ let valueType = ValueType.UNKNOWN;
776
+ if (nodeName && (name === 'src' || name === 'href') && value) {
777
+ valueType = ValueType.SRC;
778
+ } else if ((nodeName === 'img' || nodeName === 'source') && name === 'srcset') {
779
+ valueType = ValueType.SRCSET;
780
+ } else if (nodeName === 'image' && (name === 'xlink:href' || name === 'href')) {
781
+ valueType = ValueType.SRCSET;
782
+ }
783
+
784
+ const withEntitiesRef = valueType === ValueType.UNKNOWN ? ref(el => {
785
+ if (el) {
786
+ setValueWithEntities(el, value);
787
+ }
788
+ }) :
789
+ nothing;
790
+
791
+ // clang-format off
807
792
  return html`<span class="webkit-html-attribute" jslog=${jslog}><span class="webkit-html-attribute-name"
808
793
  ${animateOn(Boolean(updateRecord?.isAttributeModified(name) && !hasText), DOM_UPDATE_ANIMATION_CLASS_NAME)} ${
809
794
  relationRefDirective}>${name}</span>${
810
795
  hasText ? html`=\u200B"<span class="webkit-html-attribute-value" ${
811
796
  animateOn(
812
797
  Boolean(updateRecord?.isAttributeModified(name) && hasText),
813
- DOM_UPDATE_ANIMATION_CLASS_NAME)} ${setAttrValue} ${valueRelationRefDirective}></span>"` :
798
+ DOM_UPDATE_ANIMATION_CLASS_NAME)} ${valueRelationRefDirective} ${withEntitiesRef}>
799
+ ${valueType === ValueType.SRC ? renderLinkifiedValue(value, node) : nothing}
800
+ ${valueType === ValueType.SRCSET ? renderLinkifiedSrcset(Common.Srcset.parseSrcset(value), node) : nothing}
801
+ </span>"` :
814
802
  nothing}</span>`;
803
+ // clang-format on
815
804
  }
816
805
 
817
806
  function renderTag(
@@ -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(UI.UIUtils.MaxLengthForDisplayedURLsInConsole);
49
46
  readonly #view: View;
50
47
 
51
48
  constructor(view = DEFAULT_VIEW) {
@@ -73,11 +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
  }