chrome-devtools-frontend 1.0.1582745 → 1.0.1585538
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.
- package/front_end/core/host/InspectorFrontendHostAPI.ts +1 -0
- package/front_end/core/host/InspectorFrontendHostStub.ts +2 -0
- package/front_end/core/root/Runtime.ts +12 -11
- package/front_end/core/sdk/CSSMatchedStyles.ts +3 -0
- package/front_end/core/sdk/CSSMetadata.ts +3 -0
- package/front_end/core/sdk/NetworkManager.ts +63 -115
- package/front_end/entrypoint_template.html +1 -5
- package/front_end/entrypoints/main/MainImpl.ts +1 -0
- package/front_end/generated/Deprecation.ts +21 -0
- package/front_end/generated/InspectorBackendCommands.ts +5 -3
- package/front_end/generated/SupportedCSSProperties.js +6 -2
- package/front_end/generated/protocol-mapping.d.ts +2 -1
- package/front_end/generated/protocol-proxy-api.d.ts +2 -1
- package/front_end/generated/protocol.ts +14 -0
- package/front_end/models/ai_assistance/AiConversation.ts +34 -2
- package/front_end/models/ai_assistance/BuiltInAi.ts +1 -2
- package/front_end/models/ai_assistance/agents/AiAgent.ts +3 -10
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +1 -1
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +17 -9
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +0 -24
- package/front_end/models/ai_code_generation/AiCodeGeneration.ts +5 -0
- package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +16 -8
- package/front_end/models/computed_style/ComputedStyleModel.ts +20 -15
- package/front_end/models/greendev/Prototypes.ts +1 -5
- package/front_end/models/issues_manager/CorsIssue.ts +2 -9
- package/front_end/models/issues_manager/descriptions/corsLocalNetworkAccessPermissionDenied.md +2 -2
- package/front_end/models/javascript_metadata/NativeFunctions.js +20 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +28 -39
- package/front_end/panels/ai_assistance/components/ChatInput.ts +73 -95
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +4 -2
- package/front_end/panels/ai_assistance/components/ChatView.ts +0 -2
- package/front_end/panels/autofill/AutofillView.ts +4 -8
- package/front_end/panels/common/AiCodeGenerationTeaser.ts +37 -8
- package/front_end/panels/console/ConsoleView.ts +1 -2
- package/front_end/panels/console/ConsoleViewMessage.ts +18 -11
- package/front_end/panels/elements/ComputedStyleWidget.ts +80 -48
- package/front_end/panels/elements/ElementStatePaneWidget.ts +1 -1
- package/front_end/panels/elements/ElementsPanel.ts +18 -5
- package/front_end/panels/elements/ElementsTreeElement.ts +459 -435
- package/front_end/panels/elements/EventListenersWidget.ts +2 -4
- package/front_end/panels/elements/NodeStackTraceWidget.ts +1 -3
- package/front_end/panels/elements/PropertiesWidget.ts +1 -2
- package/front_end/panels/elements/StylePropertyTreeElement.ts +18 -2
- package/front_end/panels/elements/computedStyleWidget.css +25 -0
- package/front_end/panels/issues/AffectedResourcesView.ts +1 -2
- package/front_end/panels/network/NetworkDataGridNode.ts +0 -8
- package/front_end/panels/network/NetworkLogView.ts +64 -105
- package/front_end/panels/network/RequestConditionsDrawer.ts +168 -233
- package/front_end/panels/network/network-meta.ts +4 -27
- package/front_end/panels/settings/SettingsScreen.ts +12 -4
- package/front_end/panels/settings/WorkspaceSettingsTab.ts +1 -1
- package/front_end/panels/snippets/SnippetsQuickOpen.ts +6 -9
- package/front_end/panels/sources/CallStackSidebarPane.ts +6 -8
- package/front_end/panels/sources/DebuggerPlugin.ts +2 -1
- package/front_end/panels/sources/GoToLineQuickOpen.ts +0 -4
- package/front_end/panels/sources/OpenFileQuickOpen.ts +0 -4
- package/front_end/panels/sources/OutlineQuickOpen.ts +0 -4
- package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +3 -1
- package/front_end/panels/sources/sources-meta.ts +9 -6
- package/front_end/panels/timeline/TimelineFlameChartView.ts +0 -12
- package/front_end/panels/timeline/TimelinePanel.ts +35 -15
- package/front_end/panels/timeline/components/Sidebar.ts +21 -0
- package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +1 -20
- package/front_end/panels/timeline/components/insights/InsightRenderer.ts +0 -1
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/puppeteer/README.chromium +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/BrowsingContext.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js +2 -3
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/encoding.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/encoding.js +5 -3
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/encoding.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +8 -6
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/BrowsingContext.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js +2 -3
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/encoding.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/encoding.js +5 -3
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/encoding.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
- package/front_end/third_party/puppeteer/package/package.json +4 -4
- package/front_end/third_party/puppeteer/package/src/bidi/Page.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/bidi/core/BrowsingContext.ts +2 -2
- package/front_end/third_party/puppeteer/package/src/node/ChromeLauncher.ts +4 -3
- package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
- package/front_end/third_party/puppeteer/package/src/util/encoding.ts +5 -3
- package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
- package/front_end/ui/components/linkifier/LinkifierImpl.ts +3 -0
- package/front_end/ui/components/linkifier/LinkifierUtils.ts +3 -0
- package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +8 -0
- package/front_end/ui/components/text_editor/AiCodeGenerationParser.ts +29 -7
- package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +42 -18
- package/front_end/ui/components/text_editor/config.ts +6 -0
- package/front_end/ui/legacy/InspectorView.ts +41 -15
- package/front_end/ui/legacy/Toolbar.ts +12 -4
- package/front_end/ui/legacy/UIUtils.ts +56 -7
- package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +4 -3
- package/front_end/ui/legacy/components/quick_open/FilteredListWidget.ts +2 -1
- package/front_end/ui/legacy/components/quick_open/HelpQuickOpen.ts +8 -2
- package/front_end/ui/legacy/components/quick_open/QuickOpen.ts +8 -8
- package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +12 -9
- package/front_end/ui/legacy/components/utils/Linkifier.ts +116 -87
- package/front_end/ui/legacy/legacy.ts +0 -2
- package/front_end/ui/visual_logging/KnownContextValues.ts +4 -0
- package/mcp/HostBindings.ts +3 -0
- package/package.json +1 -1
- package/front_end/models/issues_manager/descriptions/corsInsecurePrivateNetwork.md +0 -10
- package/front_end/models/issues_manager/descriptions/corsPreflightAllowPrivateNetworkError.md +0 -10
- package/front_end/models/issues_manager/descriptions/corsPrivateNetworkPermissionDenied.md +0 -10
- package/front_end/panels/elements/computedStyleSidebarPane.css +0 -18
- package/front_end/ui/legacy/Floaty.ts +0 -438
- package/front_end/ui/legacy/floaty.css +0 -77
|
@@ -73,6 +73,7 @@ import {ImagePreviewPopover} from './ImagePreviewPopover.js';
|
|
|
73
73
|
import {getRegisteredDecorators, type MarkerDecorator, type MarkerDecoratorRegistration} from './MarkerDecorator.js';
|
|
74
74
|
|
|
75
75
|
const {html, nothing, render, Directives: {ref}} = Lit;
|
|
76
|
+
const {animateOn} = UI.UIUtils;
|
|
76
77
|
|
|
77
78
|
const UIStrings = {
|
|
78
79
|
/**
|
|
@@ -379,6 +380,15 @@ export function isOpeningTag(context: TagTypeContext): context is OpeningTagCont
|
|
|
379
380
|
}
|
|
380
381
|
|
|
381
382
|
export interface ViewInput {
|
|
383
|
+
node: SDK.DOMModel.DOMNode|null;
|
|
384
|
+
isClosingTag: boolean;
|
|
385
|
+
expanded: boolean;
|
|
386
|
+
isExpandable: boolean;
|
|
387
|
+
isXMLMimeType: boolean;
|
|
388
|
+
updateRecord: Elements.ElementUpdateRecord.ElementUpdateRecord|null;
|
|
389
|
+
onHighlightSearchResults: () => void;
|
|
390
|
+
onExpand: () => void;
|
|
391
|
+
|
|
382
392
|
containerAdornerActive: boolean;
|
|
383
393
|
flexAdornerActive: boolean;
|
|
384
394
|
gridAdornerActive: boolean;
|
|
@@ -398,7 +408,6 @@ export interface ViewInput {
|
|
|
398
408
|
showViewSourceAdorner: boolean;
|
|
399
409
|
showScrollAdorner: boolean;
|
|
400
410
|
showScrollSnapAdorner: boolean;
|
|
401
|
-
nodeInfo?: DocumentFragment;
|
|
402
411
|
topLayerIndex: number;
|
|
403
412
|
scrollSnapAdornerActive: boolean;
|
|
404
413
|
|
|
@@ -436,7 +445,7 @@ export interface ViewOutput {
|
|
|
436
445
|
|
|
437
446
|
export function adornerRef(): DirectiveResult<typeof Lit.Directives.RefDirective> {
|
|
438
447
|
let adorner: Adorners.Adorner.Adorner|undefined;
|
|
439
|
-
return ref(
|
|
448
|
+
return ref(el => {
|
|
440
449
|
if (adorner) {
|
|
441
450
|
ElementsPanel.instance().deregisterAdorner(adorner);
|
|
442
451
|
}
|
|
@@ -457,6 +466,8 @@ export interface Decoration {
|
|
|
457
466
|
color: string;
|
|
458
467
|
}
|
|
459
468
|
|
|
469
|
+
const DOM_UPDATE_ANIMATION_CLASS_NAME = 'dom-update-highlight';
|
|
470
|
+
|
|
460
471
|
function handleAdornerKeydown(cb: (event: Event) => void): (event: KeyboardEvent) => void {
|
|
461
472
|
return (event: KeyboardEvent) => {
|
|
462
473
|
if (event.code === 'Enter' || event.code === 'Space') {
|
|
@@ -467,6 +478,378 @@ function handleAdornerKeydown(cb: (event: Event) => void): (event: KeyboardEvent
|
|
|
467
478
|
};
|
|
468
479
|
}
|
|
469
480
|
|
|
481
|
+
function renderTitle(
|
|
482
|
+
node: SDK.DOMModel.DOMNode,
|
|
483
|
+
isClosingTag: boolean,
|
|
484
|
+
expanded: boolean,
|
|
485
|
+
isExpandable: boolean,
|
|
486
|
+
isXMLMimeType: boolean,
|
|
487
|
+
updateRecord: Elements.ElementUpdateRecord.ElementUpdateRecord|null,
|
|
488
|
+
onUpdateSearchHighlight: () => void,
|
|
489
|
+
onExpand: () => void,
|
|
490
|
+
): Lit.LitTemplate {
|
|
491
|
+
switch (node.nodeType()) {
|
|
492
|
+
case Node.ATTRIBUTE_NODE:
|
|
493
|
+
return renderAttribute({name: node.name as string, value: node.value as string}, updateRecord, true, node);
|
|
494
|
+
|
|
495
|
+
case Node.ELEMENT_NODE: {
|
|
496
|
+
if (node.pseudoType()) {
|
|
497
|
+
let pseudoElementName = node.nodeName();
|
|
498
|
+
const pseudoIdentifier = node.pseudoIdentifier();
|
|
499
|
+
if (pseudoIdentifier) {
|
|
500
|
+
pseudoElementName += `(${pseudoIdentifier})`;
|
|
501
|
+
}
|
|
502
|
+
return html`<span class="webkit-html-pseudo-element">${pseudoElementName}</span>\u200B`;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const tagName = node.nodeNameInCorrectCase();
|
|
506
|
+
if (isClosingTag) {
|
|
507
|
+
return renderTag(node, tagName, true, expanded, true, updateRecord);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const openingTag = renderTag(node, tagName, false, expanded, false, updateRecord);
|
|
511
|
+
|
|
512
|
+
if (isExpandable) {
|
|
513
|
+
if (!expanded) {
|
|
514
|
+
return html`${openingTag}<devtools-elements-tree-expand-button .data=${
|
|
515
|
+
{clickHandler: onExpand} as
|
|
516
|
+
ElementsComponents.ElementsTreeExpandButton
|
|
517
|
+
.ElementsTreeExpandButtonData}></devtools-elements-tree-expand-button><span style="font-size: 0;"
|
|
518
|
+
>…</span>\u200B${renderTag(node, tagName, true, expanded, false, updateRecord)}`;
|
|
519
|
+
}
|
|
520
|
+
return openingTag;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (ElementsTreeElement.canShowInlineText(node)) {
|
|
524
|
+
const firstChild = node.firstChild;
|
|
525
|
+
if (!firstChild) {
|
|
526
|
+
throw new Error('ElementsTreeElement._nodeTitleInfo expects node.firstChild to be defined.');
|
|
527
|
+
}
|
|
528
|
+
const result = convertUnicodeCharsToHTMLEntities(firstChild.nodeValue());
|
|
529
|
+
const textContent = Platform.StringUtilities.collapseWhitespace(result.text);
|
|
530
|
+
|
|
531
|
+
const renderTextNode = ref(el => {
|
|
532
|
+
if (el) {
|
|
533
|
+
el.textContent = textContent;
|
|
534
|
+
Highlighting.highlightRangesWithStyleClass(el, result.entityRanges, 'webkit-html-entity-value');
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
return html`${openingTag}<span class="webkit-html-text-node" jslog=${
|
|
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)}`;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (isXMLMimeType || !ForbiddenClosingTagElements.has(tagName)) {
|
|
547
|
+
return html`${openingTag}${renderTag(node, tagName, true, expanded, false, updateRecord)}`;
|
|
548
|
+
}
|
|
549
|
+
return openingTag;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
case Node.TEXT_NODE: {
|
|
553
|
+
if (node.parentNode && node.parentNode.nodeName().toLowerCase() === 'script') {
|
|
554
|
+
const text = node.nodeValue();
|
|
555
|
+
const highlightNode = ref(el => {
|
|
556
|
+
if (el) {
|
|
557
|
+
el.textContent = text.replace(/^[\n\r]+|\s+$/g, '');
|
|
558
|
+
void CodeHighlighter.CodeHighlighter.highlightNode(el, 'text/javascript').then(onUpdateSearchHighlight);
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
return html`<span class="webkit-html-text-node webkit-html-js-node" jslog=${
|
|
562
|
+
VisualLogging.value('script-text-node').track({change: true, dblclick: true})} ${highlightNode}></span>`;
|
|
563
|
+
}
|
|
564
|
+
if (node.parentNode && node.parentNode.nodeName().toLowerCase() === 'style') {
|
|
565
|
+
const text = node.nodeValue();
|
|
566
|
+
const highlightNode = ref(el => {
|
|
567
|
+
if (el) {
|
|
568
|
+
el.textContent = text.replace(/^[\n\r]+|\s+$/g, '');
|
|
569
|
+
void CodeHighlighter.CodeHighlighter.highlightNode(el, 'text/css').then(onUpdateSearchHighlight);
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
return html`<span class="webkit-html-text-node webkit-html-css-node" jslog=${
|
|
573
|
+
VisualLogging.value('css-text-node').track({change: true, dblclick: true})} ${highlightNode}></span>`;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const result = convertUnicodeCharsToHTMLEntities(node.nodeValue());
|
|
577
|
+
const textContent = Platform.StringUtilities.collapseWhitespace(result.text);
|
|
578
|
+
const renderTextNode = ref(el => {
|
|
579
|
+
if (el) {
|
|
580
|
+
el.textContent = textContent;
|
|
581
|
+
Highlighting.highlightRangesWithStyleClass(el, result.entityRanges, 'webkit-html-entity-value');
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
return html`"<span class="webkit-html-text-node" jslog=${VisualLogging.value('text-node').track({
|
|
585
|
+
change: true,
|
|
586
|
+
dblclick: true
|
|
587
|
+
})} ${animateOn(Boolean(updateRecord?.isCharDataModified()), DOM_UPDATE_ANIMATION_CLASS_NAME)} ${
|
|
588
|
+
renderTextNode}></span>"`;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
case Node.COMMENT_NODE: {
|
|
592
|
+
return html`<span class="webkit-html-comment"><!--${node.nodeValue()}--></span>`;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
case Node.DOCUMENT_TYPE_NODE: {
|
|
596
|
+
let doctype = '<!DOCTYPE ' + node.nodeName();
|
|
597
|
+
if (node.publicId) {
|
|
598
|
+
doctype += ' PUBLIC "' + node.publicId + '"';
|
|
599
|
+
if (node.systemId) {
|
|
600
|
+
doctype += ' "' + node.systemId + '"';
|
|
601
|
+
}
|
|
602
|
+
} else if (node.systemId) {
|
|
603
|
+
doctype += ' SYSTEM "' + node.systemId + '"';
|
|
604
|
+
}
|
|
605
|
+
if (node.internalSubset) {
|
|
606
|
+
doctype += ' [' + node.internalSubset + ']';
|
|
607
|
+
}
|
|
608
|
+
doctype += '>';
|
|
609
|
+
return html`<span class="webkit-html-doctype">${doctype}</span>`;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
case Node.CDATA_SECTION_NODE: {
|
|
613
|
+
return html`<span class="webkit-html-text-node"><![CDATA[${node.nodeValue()}]]></span>`;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
case Node.DOCUMENT_NODE: {
|
|
617
|
+
const text = (node as SDK.DOMModel.DOMDocument).documentURL;
|
|
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>`;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
case Node.DOCUMENT_FRAGMENT_NODE: {
|
|
627
|
+
return html`<span class="webkit-html-fragment">${
|
|
628
|
+
Platform.StringUtilities.collapseWhitespace(node.nodeNameInCorrectCase())}</span>`;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
default: {
|
|
632
|
+
return html`${Platform.StringUtilities.collapseWhitespace(node.nodeNameInCorrectCase())}`;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
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;
|
|
644
|
+
let highlightIndex = 0;
|
|
645
|
+
let highlightCount = 0;
|
|
646
|
+
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) {
|
|
661
|
+
result.entityRanges[highlightIndex].offset += additionalHighlightOffset;
|
|
662
|
+
++highlightIndex;
|
|
663
|
+
}
|
|
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,
|
|
672
|
+
});
|
|
673
|
+
|
|
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);
|
|
694
|
+
}
|
|
695
|
+
|
|
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
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (descriptor) {
|
|
731
|
+
UI.UIUtils.createTextChild(fragment, descriptor);
|
|
732
|
+
}
|
|
733
|
+
value = value.substring(url.length + descriptor.length);
|
|
734
|
+
}
|
|
735
|
+
return fragment;
|
|
736
|
+
}
|
|
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);
|
|
753
|
+
}
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
const relationRef =
|
|
757
|
+
(relation: Protocol.DOM.GetElementByRelationRequestRelation, tooltip: string): ReturnType<typeof ref> =>
|
|
758
|
+
ref((el): void => {
|
|
759
|
+
if (!el) {
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
void (async(): Promise<void> => {
|
|
763
|
+
const relatedElementId = await node.domModel().getElementByRelation(node.id, relation);
|
|
764
|
+
const relatedElement = node.domModel().nodeForId(relatedElementId);
|
|
765
|
+
if (!relatedElement) {
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
const link = PanelsCommon.DOMLinkifier.Linkifier.instance().linkify(relatedElement, {
|
|
769
|
+
preventKeyboardFocus: true,
|
|
770
|
+
tooltip,
|
|
771
|
+
textContent: el.textContent || undefined,
|
|
772
|
+
isDynamicLink: true,
|
|
773
|
+
});
|
|
774
|
+
el.removeChildren();
|
|
775
|
+
el.append(link);
|
|
776
|
+
})();
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
let relationRefDirective: ReturnType<typeof relationRef> = ref(() => {});
|
|
780
|
+
if (!value) {
|
|
781
|
+
if (name === 'popovertarget') {
|
|
782
|
+
relationRefDirective = relationRef(
|
|
783
|
+
Protocol.DOM.GetElementByRelationRequestRelation.PopoverTarget, i18nString(UIStrings.showPopoverTarget));
|
|
784
|
+
} else if (name === 'interesttarget') {
|
|
785
|
+
relationRefDirective = relationRef(
|
|
786
|
+
Protocol.DOM.GetElementByRelationRequestRelation.InterestTarget, i18nString(UIStrings.showInterestTarget));
|
|
787
|
+
} else if (name === 'commandfor') {
|
|
788
|
+
relationRefDirective = relationRef(
|
|
789
|
+
Protocol.DOM.GetElementByRelationRequestRelation.CommandFor, i18nString(UIStrings.showCommandForTarget));
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
let valueRelationRefDirective: ReturnType<typeof relationRef> = ref(() => {});
|
|
794
|
+
if (value) {
|
|
795
|
+
if (name === 'popovertarget') {
|
|
796
|
+
valueRelationRefDirective = relationRef(
|
|
797
|
+
Protocol.DOM.GetElementByRelationRequestRelation.PopoverTarget, i18nString(UIStrings.showPopoverTarget));
|
|
798
|
+
} else if (name === 'interesttarget') {
|
|
799
|
+
valueRelationRefDirective = relationRef(
|
|
800
|
+
Protocol.DOM.GetElementByRelationRequestRelation.InterestTarget, i18nString(UIStrings.showInterestTarget));
|
|
801
|
+
} else if (name === 'commandfor') {
|
|
802
|
+
valueRelationRefDirective = relationRef(
|
|
803
|
+
Protocol.DOM.GetElementByRelationRequestRelation.CommandFor, i18nString(UIStrings.showCommandForTarget));
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
return html`<span class="webkit-html-attribute" jslog=${jslog}><span class="webkit-html-attribute-name"
|
|
808
|
+
${animateOn(Boolean(updateRecord?.isAttributeModified(name) && !hasText), DOM_UPDATE_ANIMATION_CLASS_NAME)} ${
|
|
809
|
+
relationRefDirective}>${name}</span>${
|
|
810
|
+
hasText ? html`=\u200B"<span class="webkit-html-attribute-value" ${
|
|
811
|
+
animateOn(
|
|
812
|
+
Boolean(updateRecord?.isAttributeModified(name) && hasText),
|
|
813
|
+
DOM_UPDATE_ANIMATION_CLASS_NAME)} ${setAttrValue} ${valueRelationRefDirective}></span>"` :
|
|
814
|
+
nothing}</span>`;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
function renderTag(
|
|
818
|
+
node: SDK.DOMModel.DOMNode, tagName: string, isClosingTag: boolean, expanded: boolean,
|
|
819
|
+
isDistinctTreeElement: boolean,
|
|
820
|
+
updateRecord: Elements.ElementUpdateRecord.ElementUpdateRecord|null): Lit.LitTemplate {
|
|
821
|
+
const classMap = {
|
|
822
|
+
'webkit-html-tag': true,
|
|
823
|
+
close: isClosingTag && isDistinctTreeElement,
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
let hasUpdates = false;
|
|
827
|
+
const attributes = !isClosingTag && node.hasAttributes() ? node.attributes() : [];
|
|
828
|
+
|
|
829
|
+
if (!isClosingTag && updateRecord) {
|
|
830
|
+
hasUpdates = updateRecord.hasRemovedAttributes() || updateRecord.hasRemovedChildren();
|
|
831
|
+
hasUpdates = hasUpdates || (!expanded && updateRecord.hasChangedChildren());
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// We are taking full text content of the tag, including attributes and children, to set the aria label.
|
|
835
|
+
// FIXME: we should compute the aria label ourselves if it is event needed.
|
|
836
|
+
const setAriaLabel = ref(el => {
|
|
837
|
+
if (el?.textContent) {
|
|
838
|
+
UI.ARIAUtils.setLabel(el, el.textContent);
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
const tagNameClass = isClosingTag ? 'webkit-html-close-tag-name' : 'webkit-html-tag-name';
|
|
843
|
+
const tagString = (isClosingTag ? '/' : '') + tagName;
|
|
844
|
+
const jslog = !isClosingTag ? VisualLogging.value('tag-name').track({change: true, dblclick: true}) : '';
|
|
845
|
+
|
|
846
|
+
return html`<span
|
|
847
|
+
class=${Lit.Directives.classMap(classMap)} ${setAriaLabel}
|
|
848
|
+
><<span class=${tagNameClass} jslog=${jslog || nothing} ${
|
|
849
|
+
animateOn(hasUpdates, DOM_UPDATE_ANIMATION_CLASS_NAME)}>${tagString}</span>${
|
|
850
|
+
attributes.map(attr => html` ${renderAttribute(attr, updateRecord, false, node)}`)}></span>\u200B`;
|
|
851
|
+
}
|
|
852
|
+
|
|
470
853
|
export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLElement): void => {
|
|
471
854
|
const hasAdorners = input.showAdAdorner || input.showContainerAdorner || input.showFlexAdorner ||
|
|
472
855
|
input.showGridAdorner || input.showGridLanesAdorner || input.showMediaAdorner || input.showPopoverAdorner ||
|
|
@@ -479,7 +862,16 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
|
|
|
479
862
|
// clang-format off
|
|
480
863
|
render(html`
|
|
481
864
|
<div ${ref(el => { output.contentElement = el as HTMLElement; })}>
|
|
482
|
-
${input.
|
|
865
|
+
${input.node ? html`<span class="highlight">${renderTitle(
|
|
866
|
+
input.node,
|
|
867
|
+
input.isClosingTag,
|
|
868
|
+
input.expanded,
|
|
869
|
+
input.isExpandable,
|
|
870
|
+
input.isXMLMimeType,
|
|
871
|
+
input.updateRecord,
|
|
872
|
+
input.onHighlightSearchResults,
|
|
873
|
+
input.onExpand,
|
|
874
|
+
)}</span>` : nothing}
|
|
483
875
|
${input.isHovered || input.isSelected ? html`
|
|
484
876
|
<div class="selection fill" style=${`margin-left: ${-input.indent}px`}></div>
|
|
485
877
|
` : nothing}
|
|
@@ -681,9 +1073,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
681
1073
|
nodeInternal: SDK.DOMModel.DOMNode;
|
|
682
1074
|
override treeOutline: ElementsTreeOutline|null;
|
|
683
1075
|
|
|
684
|
-
// Handled by the view output for now.
|
|
685
|
-
contentElement!: HTMLElement;
|
|
686
|
-
|
|
687
1076
|
private searchQuery: string|null;
|
|
688
1077
|
#expandedChildrenLimit: number;
|
|
689
1078
|
private readonly decorationsThrottler: Common.Throttler.Throttler;
|
|
@@ -699,7 +1088,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
699
1088
|
readonly tagTypeContext: TagTypeContext;
|
|
700
1089
|
|
|
701
1090
|
#adornersThrottler = new Common.Throttler.Throttler(100);
|
|
702
|
-
#nodeInfo?: DocumentFragment;
|
|
703
1091
|
#containerAdornerActive = false;
|
|
704
1092
|
#flexAdornerActive = false;
|
|
705
1093
|
#gridAdornerActive = false;
|
|
@@ -713,6 +1101,12 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
713
1101
|
#descendantDecorations: Decoration[] = [];
|
|
714
1102
|
#decorationsTooltip = '';
|
|
715
1103
|
|
|
1104
|
+
#updateRecord: Elements.ElementUpdateRecord.ElementUpdateRecord|null = null;
|
|
1105
|
+
|
|
1106
|
+
// Used to add the content to TreeElement's title element.
|
|
1107
|
+
// Relied on by web tests.
|
|
1108
|
+
#contentElement?: HTMLElement;
|
|
1109
|
+
|
|
716
1110
|
constructor(node: SDK.DOMModel.DOMNode, isClosingTag?: boolean) {
|
|
717
1111
|
// The title will be updated in onattach.
|
|
718
1112
|
super();
|
|
@@ -765,7 +1159,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
765
1159
|
|
|
766
1160
|
static animateOnDOMUpdate(treeElement: ElementsTreeElement): void {
|
|
767
1161
|
const tagName = treeElement.listItemElement.querySelector('.webkit-html-tag-name');
|
|
768
|
-
UI.UIUtils.runCSSAnimationOnce(tagName || treeElement.listItemElement,
|
|
1162
|
+
UI.UIUtils.runCSSAnimationOnce(tagName || treeElement.listItemElement, DOM_UPDATE_ANIMATION_CLASS_NAME);
|
|
769
1163
|
}
|
|
770
1164
|
|
|
771
1165
|
static visibleShadowRoots(node: SDK.DOMModel.DOMNode): SDK.DOMModel.DOMNode[] {
|
|
@@ -816,9 +1210,23 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
816
1210
|
}
|
|
817
1211
|
}
|
|
818
1212
|
|
|
819
|
-
|
|
1213
|
+
// ClearNode param is used to clean DOM after in-place editing..
|
|
1214
|
+
performUpdate(clearNode = false): void {
|
|
1215
|
+
if (this.editing) {
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
const output: ViewOutput = {};
|
|
820
1219
|
DEFAULT_VIEW(
|
|
821
1220
|
{
|
|
1221
|
+
node: !clearNode ? this.nodeInternal : null,
|
|
1222
|
+
isClosingTag: this.isClosingTag(),
|
|
1223
|
+
expanded: this.expanded,
|
|
1224
|
+
isExpandable: this.isExpandable(),
|
|
1225
|
+
isXMLMimeType: Boolean(this.treeOutline?.isXMLMimeType),
|
|
1226
|
+
updateRecord: this.#updateRecord,
|
|
1227
|
+
onHighlightSearchResults: () => this.#highlightSearchResults(),
|
|
1228
|
+
onExpand: () => this.expand(),
|
|
1229
|
+
|
|
822
1230
|
containerAdornerActive: this.#containerAdornerActive,
|
|
823
1231
|
showAdAdorner: this.nodeInternal.isAdFrameNode(),
|
|
824
1232
|
showContainerAdorner: Boolean(this.#layout?.containerType) && !this.isClosingTag(),
|
|
@@ -847,7 +1255,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
847
1255
|
showSlotAdorner: Boolean(this.nodeInternal.assignedSlot) && !this.isClosingTag(),
|
|
848
1256
|
showStartingStyleAdorner: this.nodeInternal.affectedByStartingStyles() && !this.isClosingTag(),
|
|
849
1257
|
startingStyleAdornerActive: this.#startingStyleAdornerActive,
|
|
850
|
-
nodeInfo: this.#nodeInfo,
|
|
851
1258
|
onStartingStyleAdornerClick: (event: Event) => this.#onStartingStyleAdornerClick(event),
|
|
852
1259
|
onSlotAdornerClick: () => {
|
|
853
1260
|
if (this.nodeInternal.assignedSlot) {
|
|
@@ -888,7 +1295,12 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
888
1295
|
}
|
|
889
1296
|
},
|
|
890
1297
|
},
|
|
891
|
-
|
|
1298
|
+
output, this.listItemElement);
|
|
1299
|
+
|
|
1300
|
+
this.#contentElement = output.contentElement;
|
|
1301
|
+
if (this.#updateRecord) {
|
|
1302
|
+
this.#updateRecord = null;
|
|
1303
|
+
}
|
|
892
1304
|
}
|
|
893
1305
|
|
|
894
1306
|
#onContainerAdornerClick(event: Event): void {
|
|
@@ -978,7 +1390,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
978
1390
|
}
|
|
979
1391
|
}
|
|
980
1392
|
}
|
|
981
|
-
UI.UIUtils.runCSSAnimationOnce(animationElement,
|
|
1393
|
+
UI.UIUtils.runCSSAnimationOnce(animationElement, DOM_UPDATE_ANIMATION_CLASS_NAME);
|
|
982
1394
|
}
|
|
983
1395
|
|
|
984
1396
|
isClosingTag(): boolean {
|
|
@@ -1096,11 +1508,16 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1096
1508
|
this.#expandedChildrenLimit = expandedChildrenLimit;
|
|
1097
1509
|
}
|
|
1098
1510
|
|
|
1511
|
+
onTopLayerIndexChanged(): void {
|
|
1512
|
+
this.performUpdate();
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1099
1515
|
override onbind(): void {
|
|
1100
1516
|
this.performUpdate();
|
|
1101
1517
|
if (this.treeOutline && !this.isClosingTag()) {
|
|
1102
1518
|
this.treeOutline.treeElementByNode.set(this.nodeInternal, this);
|
|
1103
|
-
this.nodeInternal.addEventListener(
|
|
1519
|
+
this.nodeInternal.addEventListener(
|
|
1520
|
+
SDK.DOMModel.DOMNodeEvents.TOP_LAYER_INDEX_CHANGED, this.onTopLayerIndexChanged, this);
|
|
1104
1521
|
this.nodeInternal.addEventListener(
|
|
1105
1522
|
SDK.DOMModel.DOMNodeEvents.SCROLLABLE_FLAG_UPDATED, this.#onScrollableFlagUpdated, this);
|
|
1106
1523
|
this.nodeInternal.addEventListener(
|
|
@@ -1127,6 +1544,14 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1127
1544
|
// element is bound again.
|
|
1128
1545
|
DEFAULT_VIEW(
|
|
1129
1546
|
{
|
|
1547
|
+
node: null,
|
|
1548
|
+
isClosingTag: false,
|
|
1549
|
+
expanded: false,
|
|
1550
|
+
isExpandable: false,
|
|
1551
|
+
isXMLMimeType: false,
|
|
1552
|
+
updateRecord: null,
|
|
1553
|
+
onHighlightSearchResults: () => {},
|
|
1554
|
+
onExpand: () => {},
|
|
1130
1555
|
containerAdornerActive: false,
|
|
1131
1556
|
showAdAdorner: false,
|
|
1132
1557
|
showContainerAdorner: false,
|
|
@@ -1148,7 +1573,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1148
1573
|
showSlotAdorner: false,
|
|
1149
1574
|
showStartingStyleAdorner: false,
|
|
1150
1575
|
startingStyleAdornerActive: false,
|
|
1151
|
-
nodeInfo: this.#nodeInfo,
|
|
1152
1576
|
onStartingStyleAdornerClick: () => {},
|
|
1153
1577
|
onSlotAdornerClick: () => {},
|
|
1154
1578
|
topLayerIndex: -1,
|
|
@@ -1170,12 +1594,13 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1170
1594
|
decorationsTooltip: '',
|
|
1171
1595
|
indent: 0,
|
|
1172
1596
|
},
|
|
1173
|
-
|
|
1597
|
+
{}, this.listItemElement);
|
|
1174
1598
|
|
|
1175
1599
|
if (this.treeOutline && this.treeOutline.treeElementByNode.get(this.nodeInternal) === this) {
|
|
1176
1600
|
this.treeOutline.treeElementByNode.delete(this.nodeInternal);
|
|
1177
1601
|
}
|
|
1178
|
-
this.nodeInternal.removeEventListener(
|
|
1602
|
+
this.nodeInternal.removeEventListener(
|
|
1603
|
+
SDK.DOMModel.DOMNodeEvents.TOP_LAYER_INDEX_CHANGED, this.onTopLayerIndexChanged, this);
|
|
1179
1604
|
this.nodeInternal.removeEventListener(
|
|
1180
1605
|
SDK.DOMModel.DOMNodeEvents.SCROLLABLE_FLAG_UPDATED, this.#onScrollableFlagUpdated, this);
|
|
1181
1606
|
this.nodeInternal.removeEventListener(
|
|
@@ -1271,13 +1696,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1271
1696
|
if (this.editing) {
|
|
1272
1697
|
return false;
|
|
1273
1698
|
}
|
|
1274
|
-
const handledByFloaty = UI.Floaty.onFloatyClick({
|
|
1275
|
-
type: UI.Floaty.FloatyContextTypes.ELEMENT_NODE_ID,
|
|
1276
|
-
data: {nodeId: this.nodeInternal.id},
|
|
1277
|
-
});
|
|
1278
|
-
if (handledByFloaty) {
|
|
1279
|
-
return false;
|
|
1280
|
-
}
|
|
1281
1699
|
return super.select(omitFocus, selectedByUser);
|
|
1282
1700
|
}
|
|
1283
1701
|
|
|
@@ -1743,7 +2161,10 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1743
2161
|
// Cannot just convert the textual html into an element without
|
|
1744
2162
|
// a parent node. Use a temporary span container for the HTML.
|
|
1745
2163
|
const container = document.createElement('span');
|
|
1746
|
-
|
|
2164
|
+
|
|
2165
|
+
// eslint-disable-next-line @devtools/no-lit-render-outside-of-view
|
|
2166
|
+
Lit.render(renderAttribute({name: ' ', value: ''}, null, false, this.nodeInternal), container);
|
|
2167
|
+
const attr = container.firstElementChild as HTMLElement;
|
|
1747
2168
|
attr.style.marginLeft = '2px'; // overrides the .editing margin rule
|
|
1748
2169
|
attr.style.marginRight = '2px'; // overrides the .editing margin rule
|
|
1749
2170
|
attr.setAttribute('jslog', `${VisualLogging.value('new-attribute').track({change: true, resize: true})}`);
|
|
@@ -1789,7 +2210,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1789
2210
|
|
|
1790
2211
|
// Make sure elementForSelection is not a child of attributeValueElement.
|
|
1791
2212
|
elementForSelection =
|
|
1792
|
-
attributeValueElement
|
|
2213
|
+
attributeValueElement?.isAncestor(elementForSelection) ? attributeValueElement : elementForSelection;
|
|
1793
2214
|
|
|
1794
2215
|
function removeZeroWidthSpaceRecursive(node: Node): void {
|
|
1795
2216
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
@@ -1825,7 +2246,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1825
2246
|
return '';
|
|
1826
2247
|
}
|
|
1827
2248
|
|
|
1828
|
-
if (!Common.ParsedURL.ParsedURL.fromString(attributeValueElement
|
|
2249
|
+
if (!Common.ParsedURL.ParsedURL.fromString(attributeValueElement?.textContent || '')) {
|
|
1829
2250
|
config.setPostKeydownFinishHandler(postKeyDownFinishHandler);
|
|
1830
2251
|
}
|
|
1831
2252
|
|
|
@@ -1962,7 +2383,9 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1962
2383
|
// Hide header items.
|
|
1963
2384
|
let child: (ChildNode|null) = this.listItemElement.firstChild;
|
|
1964
2385
|
while (child) {
|
|
1965
|
-
(child
|
|
2386
|
+
if (child instanceof HTMLElement) {
|
|
2387
|
+
child.style.display = 'none';
|
|
2388
|
+
}
|
|
1966
2389
|
child = child.nextSibling;
|
|
1967
2390
|
}
|
|
1968
2391
|
// Hide children item.
|
|
@@ -2054,7 +2477,9 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
2054
2477
|
// Unhide header items.
|
|
2055
2478
|
let child: (ChildNode|null) = this.listItemElement.firstChild;
|
|
2056
2479
|
while (child) {
|
|
2057
|
-
(child
|
|
2480
|
+
if (child instanceof HTMLElement) {
|
|
2481
|
+
child.style.removeProperty('display');
|
|
2482
|
+
}
|
|
2058
2483
|
child = child.nextSibling;
|
|
2059
2484
|
}
|
|
2060
2485
|
|
|
@@ -2242,8 +2667,8 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
2242
2667
|
if (this.editing) {
|
|
2243
2668
|
return;
|
|
2244
2669
|
}
|
|
2245
|
-
|
|
2246
|
-
this.#
|
|
2670
|
+
this.performUpdate(/* clearNode= */ true);
|
|
2671
|
+
this.#updateRecord = updateRecord ?? null;
|
|
2247
2672
|
if (this.nodeInternal.nodeType() === Node.DOCUMENT_FRAGMENT_NODE && this.nodeInternal.isInShadowTree() &&
|
|
2248
2673
|
this.nodeInternal.shadowRootType()) {
|
|
2249
2674
|
this.childrenListElement.classList.add('shadow-root');
|
|
@@ -2261,8 +2686,10 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
2261
2686
|
}
|
|
2262
2687
|
}
|
|
2263
2688
|
this.performUpdate();
|
|
2264
|
-
|
|
2265
|
-
|
|
2689
|
+
if (this.#contentElement) {
|
|
2690
|
+
// fixme: we probably do not need a title element in the new tree outline.
|
|
2691
|
+
this.title = this.#contentElement;
|
|
2692
|
+
}
|
|
2266
2693
|
this.updateDecorations();
|
|
2267
2694
|
|
|
2268
2695
|
// If there is an issue with this node, make sure to update it.
|
|
@@ -2367,409 +2794,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
2367
2794
|
}
|
|
2368
2795
|
}
|
|
2369
2796
|
|
|
2370
|
-
private buildAttributeDOM(
|
|
2371
|
-
parentElement: Element|DocumentFragment, name: string, value: string,
|
|
2372
|
-
updateRecord: Elements.ElementUpdateRecord.ElementUpdateRecord|null, forceValue?: boolean,
|
|
2373
|
-
node?: SDK.DOMModel.DOMNode): HTMLElement {
|
|
2374
|
-
const closingPunctuationRegex = /[\/;:\)\]\}]/g;
|
|
2375
|
-
let highlightIndex = 0;
|
|
2376
|
-
let highlightCount = 0;
|
|
2377
|
-
let additionalHighlightOffset = 0;
|
|
2378
|
-
|
|
2379
|
-
function setValueWithEntities(this: ElementsTreeElement, element: Element, value: string): void {
|
|
2380
|
-
const result = convertUnicodeCharsToHTMLEntities(value);
|
|
2381
|
-
highlightCount = result.entityRanges.length;
|
|
2382
|
-
value = result.text.replace(closingPunctuationRegex, (match, replaceOffset) => {
|
|
2383
|
-
while (highlightIndex < highlightCount && result.entityRanges[highlightIndex].offset < replaceOffset) {
|
|
2384
|
-
result.entityRanges[highlightIndex].offset += additionalHighlightOffset;
|
|
2385
|
-
++highlightIndex;
|
|
2386
|
-
}
|
|
2387
|
-
additionalHighlightOffset += 1;
|
|
2388
|
-
return match + '\u200B';
|
|
2389
|
-
});
|
|
2390
|
-
|
|
2391
|
-
while (highlightIndex < highlightCount) {
|
|
2392
|
-
result.entityRanges[highlightIndex].offset += additionalHighlightOffset;
|
|
2393
|
-
++highlightIndex;
|
|
2394
|
-
}
|
|
2395
|
-
element.setTextContentTruncatedIfNeeded(value);
|
|
2396
|
-
Highlighting.highlightRangesWithStyleClass(element, result.entityRanges, 'webkit-html-entity-value');
|
|
2397
|
-
}
|
|
2398
|
-
|
|
2399
|
-
const hasText = (forceValue || value.length > 0);
|
|
2400
|
-
const attrSpanElement = parentElement.createChild('span', 'webkit-html-attribute');
|
|
2401
|
-
attrSpanElement.setAttribute(
|
|
2402
|
-
'jslog', `${VisualLogging.value(name === 'style' ? 'style-attribute' : 'attribute').track({
|
|
2403
|
-
change: true,
|
|
2404
|
-
dblclick: true,
|
|
2405
|
-
})}`);
|
|
2406
|
-
|
|
2407
|
-
const attrNameElement = attrSpanElement.createChild('span', 'webkit-html-attribute-name');
|
|
2408
|
-
attrNameElement.textContent = name;
|
|
2409
|
-
|
|
2410
|
-
if (hasText) {
|
|
2411
|
-
UI.UIUtils.createTextChild(attrSpanElement, '=\u200B"');
|
|
2412
|
-
}
|
|
2413
|
-
|
|
2414
|
-
const attrValueElement = attrSpanElement.createChild('span', 'webkit-html-attribute-value');
|
|
2415
|
-
|
|
2416
|
-
if (updateRecord?.isAttributeModified(name)) {
|
|
2417
|
-
UI.UIUtils.runCSSAnimationOnce(hasText ? attrValueElement : attrNameElement, 'dom-update-highlight');
|
|
2418
|
-
}
|
|
2419
|
-
|
|
2420
|
-
function linkifyValue(this: ElementsTreeElement, value: string): Element {
|
|
2421
|
-
const rewrittenHref = node ? node.resolveURL(value) : null;
|
|
2422
|
-
if (rewrittenHref === null) {
|
|
2423
|
-
const span = document.createElement('span');
|
|
2424
|
-
setValueWithEntities.call(this, span, value);
|
|
2425
|
-
return span;
|
|
2426
|
-
}
|
|
2427
|
-
value = value.replace(closingPunctuationRegex, '$&\u200B');
|
|
2428
|
-
if (value.startsWith('data:')) {
|
|
2429
|
-
value = Platform.StringUtilities.trimMiddle(value, 60);
|
|
2430
|
-
}
|
|
2431
|
-
const link = node && node.nodeName().toLowerCase() === 'a' ?
|
|
2432
|
-
Link.create(rewrittenHref, value, undefined, 'image-url') :
|
|
2433
|
-
Components.Linkifier.Linkifier.linkifyURL(rewrittenHref, {
|
|
2434
|
-
text: value,
|
|
2435
|
-
preventClick: true,
|
|
2436
|
-
showColumnNumber: false,
|
|
2437
|
-
inlineFrameIndex: 0,
|
|
2438
|
-
});
|
|
2439
|
-
return ImagePreviewPopover.setImageUrl(link, rewrittenHref);
|
|
2440
|
-
}
|
|
2441
|
-
|
|
2442
|
-
const nodeName = node ? node.nodeName().toLowerCase() : '';
|
|
2443
|
-
// If the href/src attribute has a value, attempt to link it.
|
|
2444
|
-
// There's no point trying to link it if the value is empty (e.g. <a href=''>).
|
|
2445
|
-
if (nodeName && (name === 'src' || name === 'href') && value) {
|
|
2446
|
-
attrValueElement.appendChild(linkifyValue.call(this, value));
|
|
2447
|
-
} else if ((nodeName === 'img' || nodeName === 'source') && name === 'srcset') {
|
|
2448
|
-
attrValueElement.appendChild(linkifySrcset.call(this, value));
|
|
2449
|
-
} else if (nodeName === 'image' && (name === 'xlink:href' || name === 'href')) {
|
|
2450
|
-
attrValueElement.appendChild(linkifySrcset.call(this, value));
|
|
2451
|
-
} else {
|
|
2452
|
-
setValueWithEntities.call(this, attrValueElement, value);
|
|
2453
|
-
}
|
|
2454
|
-
|
|
2455
|
-
switch (name) {
|
|
2456
|
-
case 'popovertarget': {
|
|
2457
|
-
const linkedPart = value ? attrValueElement : attrNameElement;
|
|
2458
|
-
void this.linkifyElementByRelation(
|
|
2459
|
-
linkedPart, Protocol.DOM.GetElementByRelationRequestRelation.PopoverTarget,
|
|
2460
|
-
i18nString(UIStrings.showPopoverTarget));
|
|
2461
|
-
break;
|
|
2462
|
-
}
|
|
2463
|
-
case 'interesttarget': {
|
|
2464
|
-
const linkedPart = value ? attrValueElement : attrNameElement;
|
|
2465
|
-
void this.linkifyElementByRelation(
|
|
2466
|
-
linkedPart, Protocol.DOM.GetElementByRelationRequestRelation.InterestTarget,
|
|
2467
|
-
i18nString(UIStrings.showInterestTarget));
|
|
2468
|
-
break;
|
|
2469
|
-
}
|
|
2470
|
-
case 'commandfor': {
|
|
2471
|
-
const linkedPart = value ? attrValueElement : attrNameElement;
|
|
2472
|
-
void this.linkifyElementByRelation(
|
|
2473
|
-
linkedPart, Protocol.DOM.GetElementByRelationRequestRelation.CommandFor,
|
|
2474
|
-
i18nString(UIStrings.showCommandForTarget));
|
|
2475
|
-
break;
|
|
2476
|
-
}
|
|
2477
|
-
}
|
|
2478
|
-
|
|
2479
|
-
if (hasText) {
|
|
2480
|
-
UI.UIUtils.createTextChild(attrSpanElement, '"');
|
|
2481
|
-
}
|
|
2482
|
-
|
|
2483
|
-
function linkifySrcset(this: ElementsTreeElement, value: string): DocumentFragment {
|
|
2484
|
-
// Splitting normally on commas or spaces will break on valid srcsets "foo 1x,bar 2x" and "data:,foo 1x".
|
|
2485
|
-
// 1) Let the index of the next space be `indexOfSpace`.
|
|
2486
|
-
// 2a) If the character at `indexOfSpace - 1` is a comma, collect the preceding characters up to
|
|
2487
|
-
// `indexOfSpace - 1` as a URL and repeat step 1).
|
|
2488
|
-
// 2b) Else, collect the preceding characters as a URL.
|
|
2489
|
-
// 3) Collect the characters from `indexOfSpace` up to the next comma as the size descriptor and repeat step 1).
|
|
2490
|
-
// https://html.spec.whatwg.org/C/#parse-a-srcset-attribute
|
|
2491
|
-
const fragment = document.createDocumentFragment();
|
|
2492
|
-
let i = 0;
|
|
2493
|
-
while (value.length) {
|
|
2494
|
-
if (i++ > 0) {
|
|
2495
|
-
UI.UIUtils.createTextChild(fragment, ' ');
|
|
2496
|
-
}
|
|
2497
|
-
value = value.trim();
|
|
2498
|
-
// The url and descriptor may end with a separating comma.
|
|
2499
|
-
let url = '';
|
|
2500
|
-
let descriptor = '';
|
|
2501
|
-
const indexOfSpace = value.search(/\s/);
|
|
2502
|
-
if (indexOfSpace === -1) {
|
|
2503
|
-
url = value;
|
|
2504
|
-
} else if (indexOfSpace > 0 && value[indexOfSpace - 1] === ',') {
|
|
2505
|
-
url = value.substring(0, indexOfSpace);
|
|
2506
|
-
} else {
|
|
2507
|
-
url = value.substring(0, indexOfSpace);
|
|
2508
|
-
const indexOfComma = value.indexOf(',', indexOfSpace);
|
|
2509
|
-
if (indexOfComma !== -1) {
|
|
2510
|
-
descriptor = value.substring(indexOfSpace, indexOfComma + 1);
|
|
2511
|
-
} else {
|
|
2512
|
-
descriptor = value.substring(indexOfSpace);
|
|
2513
|
-
}
|
|
2514
|
-
}
|
|
2515
|
-
|
|
2516
|
-
if (url) {
|
|
2517
|
-
// Up to one trailing comma should be removed from `url`.
|
|
2518
|
-
if (url.endsWith(',')) {
|
|
2519
|
-
fragment.appendChild(linkifyValue.call(this, url.substring(0, url.length - 1)));
|
|
2520
|
-
UI.UIUtils.createTextChild(fragment, ',');
|
|
2521
|
-
} else {
|
|
2522
|
-
fragment.appendChild(linkifyValue.call(this, url));
|
|
2523
|
-
}
|
|
2524
|
-
}
|
|
2525
|
-
if (descriptor) {
|
|
2526
|
-
UI.UIUtils.createTextChild(fragment, descriptor);
|
|
2527
|
-
}
|
|
2528
|
-
value = value.substring(url.length + descriptor.length);
|
|
2529
|
-
}
|
|
2530
|
-
return fragment;
|
|
2531
|
-
}
|
|
2532
|
-
|
|
2533
|
-
return attrSpanElement;
|
|
2534
|
-
}
|
|
2535
|
-
|
|
2536
|
-
private async linkifyElementByRelation(
|
|
2537
|
-
linkContainer: Element, relation: Protocol.DOM.GetElementByRelationRequestRelation,
|
|
2538
|
-
tooltip: string): Promise<void> {
|
|
2539
|
-
const relatedElementId = await this.nodeInternal.domModel().getElementByRelation(this.nodeInternal.id, relation);
|
|
2540
|
-
const relatedElement = this.nodeInternal.domModel().nodeForId(relatedElementId);
|
|
2541
|
-
if (!relatedElement) {
|
|
2542
|
-
return;
|
|
2543
|
-
}
|
|
2544
|
-
const link = PanelsCommon.DOMLinkifier.Linkifier.instance().linkify(relatedElement, {
|
|
2545
|
-
preventKeyboardFocus: true,
|
|
2546
|
-
tooltip,
|
|
2547
|
-
textContent: linkContainer.textContent || undefined,
|
|
2548
|
-
isDynamicLink: true,
|
|
2549
|
-
});
|
|
2550
|
-
linkContainer.removeChildren();
|
|
2551
|
-
linkContainer.append(link);
|
|
2552
|
-
}
|
|
2553
|
-
|
|
2554
|
-
private buildPseudoElementDOM(parentElement: DocumentFragment, pseudoElementName: string): void {
|
|
2555
|
-
const pseudoElement = parentElement.createChild('span', 'webkit-html-pseudo-element');
|
|
2556
|
-
pseudoElement.textContent = pseudoElementName;
|
|
2557
|
-
UI.UIUtils.createTextChild(parentElement, '\u200B');
|
|
2558
|
-
}
|
|
2559
|
-
|
|
2560
|
-
private buildTagDOM(
|
|
2561
|
-
parentElement: DocumentFragment, tagName: string, isClosingTag: boolean, isDistinctTreeElement: boolean,
|
|
2562
|
-
updateRecord: Elements.ElementUpdateRecord.ElementUpdateRecord|null): void {
|
|
2563
|
-
const node = this.nodeInternal;
|
|
2564
|
-
const classes = ['webkit-html-tag'];
|
|
2565
|
-
if (isClosingTag && isDistinctTreeElement) {
|
|
2566
|
-
classes.push('close');
|
|
2567
|
-
}
|
|
2568
|
-
const tagElement = parentElement.createChild('span', classes.join(' '));
|
|
2569
|
-
UI.UIUtils.createTextChild(tagElement, '<');
|
|
2570
|
-
const tagNameElement =
|
|
2571
|
-
tagElement.createChild('span', isClosingTag ? 'webkit-html-close-tag-name' : 'webkit-html-tag-name');
|
|
2572
|
-
if (!isClosingTag) {
|
|
2573
|
-
tagNameElement.setAttribute('jslog', `${VisualLogging.value('tag-name').track({change: true, dblclick: true})}`);
|
|
2574
|
-
}
|
|
2575
|
-
tagNameElement.textContent = (isClosingTag ? '/' : '') + tagName;
|
|
2576
|
-
if (!isClosingTag) {
|
|
2577
|
-
if (node.hasAttributes()) {
|
|
2578
|
-
const attributes = node.attributes();
|
|
2579
|
-
for (let i = 0; i < attributes.length; ++i) {
|
|
2580
|
-
const attr = attributes[i];
|
|
2581
|
-
UI.UIUtils.createTextChild(tagElement, ' ');
|
|
2582
|
-
this.buildAttributeDOM(tagElement, attr.name, attr.value, updateRecord, false, node);
|
|
2583
|
-
}
|
|
2584
|
-
}
|
|
2585
|
-
if (updateRecord) {
|
|
2586
|
-
let hasUpdates: boolean = updateRecord.hasRemovedAttributes() || updateRecord.hasRemovedChildren();
|
|
2587
|
-
hasUpdates = hasUpdates || (!this.expanded && updateRecord.hasChangedChildren());
|
|
2588
|
-
if (hasUpdates) {
|
|
2589
|
-
UI.UIUtils.runCSSAnimationOnce(tagNameElement, 'dom-update-highlight');
|
|
2590
|
-
}
|
|
2591
|
-
}
|
|
2592
|
-
}
|
|
2593
|
-
|
|
2594
|
-
UI.UIUtils.createTextChild(tagElement, '>');
|
|
2595
|
-
UI.UIUtils.createTextChild(parentElement, '\u200B');
|
|
2596
|
-
if (tagElement.textContent) {
|
|
2597
|
-
UI.ARIAUtils.setLabel(tagElement, tagElement.textContent);
|
|
2598
|
-
}
|
|
2599
|
-
}
|
|
2600
|
-
|
|
2601
|
-
private nodeTitleInfo(updateRecord: Elements.ElementUpdateRecord.ElementUpdateRecord|null): DocumentFragment {
|
|
2602
|
-
const node = this.nodeInternal;
|
|
2603
|
-
const titleDOM = document.createDocumentFragment();
|
|
2604
|
-
const updateSearchHighlight = (): void => {
|
|
2605
|
-
this.#highlightSearchResults();
|
|
2606
|
-
};
|
|
2607
|
-
|
|
2608
|
-
switch (node.nodeType()) {
|
|
2609
|
-
case Node.ATTRIBUTE_NODE:
|
|
2610
|
-
this.buildAttributeDOM(titleDOM, (node.name as string), (node.value as string), updateRecord, true);
|
|
2611
|
-
break;
|
|
2612
|
-
|
|
2613
|
-
case Node.ELEMENT_NODE: {
|
|
2614
|
-
if (node.pseudoType()) {
|
|
2615
|
-
let pseudoElementName = node.nodeName();
|
|
2616
|
-
const pseudoIdentifier = node.pseudoIdentifier();
|
|
2617
|
-
if (pseudoIdentifier) {
|
|
2618
|
-
pseudoElementName += `(${pseudoIdentifier})`;
|
|
2619
|
-
}
|
|
2620
|
-
this.buildPseudoElementDOM(titleDOM, pseudoElementName);
|
|
2621
|
-
break;
|
|
2622
|
-
}
|
|
2623
|
-
|
|
2624
|
-
const tagName = node.nodeNameInCorrectCase();
|
|
2625
|
-
if (this.isClosingTag()) {
|
|
2626
|
-
this.buildTagDOM(titleDOM, tagName, true, true, updateRecord);
|
|
2627
|
-
break;
|
|
2628
|
-
}
|
|
2629
|
-
|
|
2630
|
-
this.buildTagDOM(titleDOM, tagName, false, false, updateRecord);
|
|
2631
|
-
|
|
2632
|
-
if (this.isExpandable()) {
|
|
2633
|
-
if (!this.expanded) {
|
|
2634
|
-
const expandButton = new ElementsComponents.ElementsTreeExpandButton.ElementsTreeExpandButton();
|
|
2635
|
-
expandButton.data = {
|
|
2636
|
-
clickHandler: () => this.expand(),
|
|
2637
|
-
};
|
|
2638
|
-
titleDOM.appendChild(expandButton);
|
|
2639
|
-
|
|
2640
|
-
// This hidden span with … is for blink layout tests.
|
|
2641
|
-
// The method dumpElementsTree(front_end/legacy_test_runner/elements_test_runner/ElementsTestRunner.js)
|
|
2642
|
-
// dumps … to identify expandable element.
|
|
2643
|
-
const hidden = document.createElement('span');
|
|
2644
|
-
hidden.textContent = '…';
|
|
2645
|
-
hidden.style.fontSize = '0';
|
|
2646
|
-
titleDOM.appendChild(hidden);
|
|
2647
|
-
|
|
2648
|
-
UI.UIUtils.createTextChild(titleDOM, '\u200B');
|
|
2649
|
-
this.buildTagDOM(titleDOM, tagName, true, false, updateRecord);
|
|
2650
|
-
}
|
|
2651
|
-
break;
|
|
2652
|
-
}
|
|
2653
|
-
|
|
2654
|
-
if (ElementsTreeElement.canShowInlineText(node)) {
|
|
2655
|
-
const textNodeElement = titleDOM.createChild('span', 'webkit-html-text-node');
|
|
2656
|
-
textNodeElement.setAttribute(
|
|
2657
|
-
'jslog', `${VisualLogging.value('text-node').track({change: true, dblclick: true})}`);
|
|
2658
|
-
const firstChild = node.firstChild;
|
|
2659
|
-
if (!firstChild) {
|
|
2660
|
-
throw new Error('ElementsTreeElement._nodeTitleInfo expects node.firstChild to be defined.');
|
|
2661
|
-
}
|
|
2662
|
-
const result = convertUnicodeCharsToHTMLEntities(firstChild.nodeValue());
|
|
2663
|
-
textNodeElement.textContent = Platform.StringUtilities.collapseWhitespace(result.text);
|
|
2664
|
-
Highlighting.highlightRangesWithStyleClass(textNodeElement, result.entityRanges, 'webkit-html-entity-value');
|
|
2665
|
-
UI.UIUtils.createTextChild(titleDOM, '\u200B');
|
|
2666
|
-
this.buildTagDOM(titleDOM, tagName, true, false, updateRecord);
|
|
2667
|
-
if (updateRecord?.hasChangedChildren()) {
|
|
2668
|
-
UI.UIUtils.runCSSAnimationOnce(textNodeElement, 'dom-update-highlight');
|
|
2669
|
-
}
|
|
2670
|
-
if (updateRecord?.isCharDataModified()) {
|
|
2671
|
-
UI.UIUtils.runCSSAnimationOnce(textNodeElement, 'dom-update-highlight');
|
|
2672
|
-
}
|
|
2673
|
-
break;
|
|
2674
|
-
}
|
|
2675
|
-
|
|
2676
|
-
if (this.treeOutline?.isXMLMimeType || !ForbiddenClosingTagElements.has(tagName)) {
|
|
2677
|
-
this.buildTagDOM(titleDOM, tagName, true, false, updateRecord);
|
|
2678
|
-
}
|
|
2679
|
-
break;
|
|
2680
|
-
}
|
|
2681
|
-
|
|
2682
|
-
case Node.TEXT_NODE:
|
|
2683
|
-
if (node.parentNode && node.parentNode.nodeName().toLowerCase() === 'script') {
|
|
2684
|
-
const newNode = titleDOM.createChild('span', 'webkit-html-text-node webkit-html-js-node');
|
|
2685
|
-
newNode.setAttribute(
|
|
2686
|
-
'jslog', `${VisualLogging.value('script-text-node').track({change: true, dblclick: true})}`);
|
|
2687
|
-
const text = node.nodeValue();
|
|
2688
|
-
newNode.textContent = text.replace(/^[\n\r]+|\s+$/g, '');
|
|
2689
|
-
void CodeHighlighter.CodeHighlighter.highlightNode(newNode, 'text/javascript').then(updateSearchHighlight);
|
|
2690
|
-
} else if (node.parentNode && node.parentNode.nodeName().toLowerCase() === 'style') {
|
|
2691
|
-
const newNode = titleDOM.createChild('span', 'webkit-html-text-node webkit-html-css-node');
|
|
2692
|
-
newNode.setAttribute(
|
|
2693
|
-
'jslog', `${VisualLogging.value('css-text-node').track({change: true, dblclick: true})}`);
|
|
2694
|
-
const text = node.nodeValue();
|
|
2695
|
-
newNode.textContent = text.replace(/^[\n\r]+|\s+$/g, '');
|
|
2696
|
-
void CodeHighlighter.CodeHighlighter.highlightNode(newNode, 'text/css').then(updateSearchHighlight);
|
|
2697
|
-
} else {
|
|
2698
|
-
UI.UIUtils.createTextChild(titleDOM, '"');
|
|
2699
|
-
const textNodeElement = titleDOM.createChild('span', 'webkit-html-text-node');
|
|
2700
|
-
textNodeElement.setAttribute(
|
|
2701
|
-
'jslog', `${VisualLogging.value('text-node').track({change: true, dblclick: true})}`);
|
|
2702
|
-
const result = convertUnicodeCharsToHTMLEntities(node.nodeValue());
|
|
2703
|
-
textNodeElement.textContent = Platform.StringUtilities.collapseWhitespace(result.text);
|
|
2704
|
-
Highlighting.highlightRangesWithStyleClass(textNodeElement, result.entityRanges, 'webkit-html-entity-value');
|
|
2705
|
-
UI.UIUtils.createTextChild(titleDOM, '"');
|
|
2706
|
-
if (updateRecord?.isCharDataModified()) {
|
|
2707
|
-
UI.UIUtils.runCSSAnimationOnce(textNodeElement, 'dom-update-highlight');
|
|
2708
|
-
}
|
|
2709
|
-
}
|
|
2710
|
-
break;
|
|
2711
|
-
|
|
2712
|
-
case Node.COMMENT_NODE: {
|
|
2713
|
-
const commentElement = titleDOM.createChild('span', 'webkit-html-comment');
|
|
2714
|
-
UI.UIUtils.createTextChild(commentElement, '<!--' + node.nodeValue() + '-->');
|
|
2715
|
-
break;
|
|
2716
|
-
}
|
|
2717
|
-
|
|
2718
|
-
case Node.DOCUMENT_TYPE_NODE: {
|
|
2719
|
-
const docTypeElement = titleDOM.createChild('span', 'webkit-html-doctype');
|
|
2720
|
-
UI.UIUtils.createTextChild(docTypeElement, '<!DOCTYPE ' + node.nodeName());
|
|
2721
|
-
if (node.publicId) {
|
|
2722
|
-
UI.UIUtils.createTextChild(docTypeElement, ' PUBLIC "' + node.publicId + '"');
|
|
2723
|
-
if (node.systemId) {
|
|
2724
|
-
UI.UIUtils.createTextChild(docTypeElement, ' "' + node.systemId + '"');
|
|
2725
|
-
}
|
|
2726
|
-
} else if (node.systemId) {
|
|
2727
|
-
UI.UIUtils.createTextChild(docTypeElement, ' SYSTEM "' + node.systemId + '"');
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
|
-
if (node.internalSubset) {
|
|
2731
|
-
UI.UIUtils.createTextChild(docTypeElement, ' [' + node.internalSubset + ']');
|
|
2732
|
-
}
|
|
2733
|
-
|
|
2734
|
-
UI.UIUtils.createTextChild(docTypeElement, '>');
|
|
2735
|
-
break;
|
|
2736
|
-
}
|
|
2737
|
-
|
|
2738
|
-
case Node.CDATA_SECTION_NODE: {
|
|
2739
|
-
const cdataElement = titleDOM.createChild('span', 'webkit-html-text-node');
|
|
2740
|
-
UI.UIUtils.createTextChild(cdataElement, '<![CDATA[' + node.nodeValue() + ']]>');
|
|
2741
|
-
break;
|
|
2742
|
-
}
|
|
2743
|
-
|
|
2744
|
-
case Node.DOCUMENT_NODE: {
|
|
2745
|
-
const documentElement = titleDOM.createChild('span');
|
|
2746
|
-
UI.UIUtils.createTextChild(documentElement, '#document (');
|
|
2747
|
-
const text = (node as SDK.DOMModel.DOMDocument).documentURL;
|
|
2748
|
-
documentElement.appendChild(Components.Linkifier.Linkifier.linkifyURL(text, {
|
|
2749
|
-
text,
|
|
2750
|
-
preventClick: true,
|
|
2751
|
-
showColumnNumber: false,
|
|
2752
|
-
inlineFrameIndex: 0,
|
|
2753
|
-
}));
|
|
2754
|
-
UI.UIUtils.createTextChild(documentElement, ')');
|
|
2755
|
-
break;
|
|
2756
|
-
}
|
|
2757
|
-
|
|
2758
|
-
case Node.DOCUMENT_FRAGMENT_NODE: {
|
|
2759
|
-
const fragmentElement = titleDOM.createChild('span', 'webkit-html-fragment');
|
|
2760
|
-
fragmentElement.textContent = Platform.StringUtilities.collapseWhitespace(node.nodeNameInCorrectCase());
|
|
2761
|
-
break;
|
|
2762
|
-
}
|
|
2763
|
-
|
|
2764
|
-
default: {
|
|
2765
|
-
const nameWithSpaceCollapsed = Platform.StringUtilities.collapseWhitespace(node.nodeNameInCorrectCase());
|
|
2766
|
-
UI.UIUtils.createTextChild(titleDOM, nameWithSpaceCollapsed);
|
|
2767
|
-
}
|
|
2768
|
-
}
|
|
2769
|
-
|
|
2770
|
-
return titleDOM;
|
|
2771
|
-
}
|
|
2772
|
-
|
|
2773
2797
|
async remove(): Promise<void> {
|
|
2774
2798
|
if (this.treeOutline?.isToggledToHidden(this.nodeInternal)) {
|
|
2775
2799
|
// Unhide the node before removing. This avoids inconsistent state if the node is restored via undo.
|