chrome-devtools-frontend 1.0.1679704 → 1.0.1681091

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 (54) hide show
  1. package/AUTHORS +1 -0
  2. package/front_end/core/platform/TypescriptUtilities.ts +0 -9
  3. package/front_end/core/platform/platform.ts +1 -1
  4. package/front_end/core/sdk/ConsoleModel.ts +6 -3
  5. package/front_end/core/sdk/DOMModel.ts +102 -0
  6. package/front_end/core/sdk/EmulationModel.ts +4 -2
  7. package/front_end/core/sdk/NetworkManager.ts +7 -6
  8. package/front_end/core/sdk/PageResourceLoader.ts +4 -3
  9. package/front_end/core/sdk/RuntimeModel.ts +5 -3
  10. package/front_end/core/sdk/SDKSettings.ts +47 -0
  11. package/front_end/core/sdk/sdk-meta.ts +0 -138
  12. package/front_end/entrypoints/inspector_main/RenderingOptions.ts +4 -2
  13. package/front_end/entrypoints/inspector_main/inspector_main-meta.ts +32 -0
  14. package/front_end/entrypoints/main/MainImpl.ts +1 -1
  15. package/front_end/generated/InspectorBackendCommands.ts +3 -1
  16. package/front_end/generated/SupportedCSSProperties.ts +20 -0
  17. package/front_end/generated/protocol-mapping.d.ts +7 -0
  18. package/front_end/generated/protocol-proxy-api.d.ts +5 -0
  19. package/front_end/generated/protocol.ts +15 -3
  20. package/front_end/panels/console/ConsoleView.ts +2 -1
  21. package/front_end/panels/console/console-meta.ts +10 -5
  22. package/front_end/panels/elements/DOMTreeContextMenu.ts +541 -0
  23. package/front_end/panels/elements/ElementsTreeElement.ts +27 -494
  24. package/front_end/panels/elements/ElementsTreeOutline.ts +173 -287
  25. package/front_end/panels/elements/StylesSidebarPane.ts +2 -1
  26. package/front_end/panels/elements/elements.ts +3 -0
  27. package/front_end/panels/issues/CorsIssueDetailsView.ts +13 -9
  28. package/front_end/panels/mobile_throttling/NetworkPanelIndicator.ts +3 -1
  29. package/front_end/panels/network/NetworkConfigView.ts +2 -1
  30. package/front_end/panels/network/NetworkPanel.ts +1 -1
  31. package/front_end/panels/network/network-meta.ts +60 -0
  32. package/front_end/panels/profiler/HeapDetachedElementsDataGrid.ts +2 -1
  33. package/front_end/panels/profiler/ProfileLauncherView.ts +19 -16
  34. package/front_end/panels/profiler/ProfileSidebarTreeElement.ts +1 -1
  35. package/front_end/panels/security/security.ts +6 -0
  36. package/front_end/panels/sources/SourcesView.ts +8 -107
  37. package/front_end/panels/sources/TabbedEditorContainer.ts +110 -7
  38. package/front_end/panels/sources/sources-meta.ts +16 -0
  39. package/front_end/panels/timeline/TimelineLoader.ts +1 -1
  40. package/front_end/panels/timeline/TimelineUIUtils.ts +6 -0
  41. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +4 -3
  42. package/front_end/panels/timeline/components/LiveMetricsView.ts +1 -1
  43. package/front_end/panels/timeline/components/MetricCard.ts +4 -3
  44. package/front_end/panels/timeline/components/NetworkRequestDetails.ts +5 -2
  45. package/front_end/panels/timeline/components/TimelineRangeSummaryView.ts +1 -1
  46. package/front_end/panels/timeline/components/TimelineSummary.ts +8 -5
  47. package/front_end/third_party/chromium/README.chromium +1 -1
  48. package/front_end/ui/legacy/TabbedPane.ts +34 -2
  49. package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +5 -5
  50. package/front_end/ui/legacy/components/quick_open/FilteredListWidget.ts +7 -7
  51. package/front_end/ui/legacy/components/quick_open/QuickOpen.ts +1 -1
  52. package/front_end/ui/legacy/components/quick_open/quick_open-meta.ts +2 -2
  53. package/front_end/ui/visual_logging/KnownContextValues.ts +29 -0
  54. package/package.json +1 -1
@@ -49,8 +49,8 @@ import {html, nothing, render} from '../../ui/lit/lit.js';
49
49
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
50
50
 
51
51
  import {AdoptedStyleSheetSetTreeElement, AdoptedStyleSheetTreeElement} from './AdoptedStyleSheetTreeElement.js';
52
+ import {showContextMenu} from './DOMTreeContextMenu.js';
52
53
  import {getElementIssueDetails} from './ElementIssueUtils.js';
53
- import {ElementsPanel} from './ElementsPanel.js';
54
54
  import {ElementsTreeElement, InitialChildrenLimit, isOpeningTag} from './ElementsTreeElement.js';
55
55
  import elementsTreeOutlineStyles from './elementsTreeOutline.css.js';
56
56
  import {ImagePreviewPopover} from './ImagePreviewPopover.js';
@@ -63,10 +63,6 @@ const UIStrings = {
63
63
  * @description ARIA accessible name in Elements Tree Outline of the Elements panel
64
64
  */
65
65
  pageDom: 'Page DOM',
66
- /**
67
- * @description A context menu item to store a value as a global variable the Elements Panel
68
- */
69
- storeAsGlobalVariable: 'Store as global variable',
70
66
  /**
71
67
  * @description Tree element expand all button element button text content in Elements Tree Outline of the Elements panel
72
68
  * @example {3} PH1
@@ -117,10 +113,12 @@ interface ViewInput {
117
113
  onElementsTreeUpdated: (event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode[]>) => void;
118
114
  onElementCollapsed: () => void;
119
115
  onElementExpanded: () => void;
116
+ onSaveNodeToTempVariable?: (node: SDK.DOMModel.DOMNode) => void;
120
117
  }
121
118
 
122
119
  interface ViewOutput {
123
120
  elementsTreeOutline?: ElementsTreeOutline;
121
+ imagePreviewPopover?: ImagePreviewPopover;
124
122
  highlightedTreeElement: ElementsTreeElement|null;
125
123
  isUpdatingHighlights: boolean;
126
124
  alreadyExpandedParentTreeElement: ElementsTreeElement|null;
@@ -131,27 +129,102 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
131
129
  // FIXME: this is basically a ref to existing imperative
132
130
  // implementation. Once this is declarative the ref should not be
133
131
  // needed.
134
- output.elementsTreeOutline = new ElementsTreeOutline(
135
- input.omitRootDOMNode, input.selectEnabled, input.hideGutter, input.maxTreeDepth, input.enableContextMenu,
136
- input.showComments, input.showAIButton, input.disableEdits, input.expandRoot);
137
- output.elementsTreeOutline.addEventListener(
138
- ElementsTreeOutline.Events.SelectedNodeChanged, input.onSelectedNodeChanged, this);
139
- output.elementsTreeOutline.addEventListener(
140
- ElementsTreeOutline.Events.ElementsTreeUpdated, input.onElementsTreeUpdated, this);
141
- output.elementsTreeOutline.addEventListener(UI.TreeOutline.Events.ElementExpanded, input.onElementExpanded, this);
142
- output.elementsTreeOutline.addEventListener(UI.TreeOutline.Events.ElementCollapsed, input.onElementCollapsed, this);
143
- output.elementsTreeOutline.addEventListener(ElementsTreeOutline.Events.ShowAllRows, () => {
144
- if (output.elementsTreeOutline?.maxRowsShown) {
132
+ const elementsTreeOutline = new ElementsTreeOutline(input.omitRootDOMNode, input.selectEnabled, input.hideGutter,
133
+ input.maxTreeDepth, input.enableContextMenu, input.showComments,
134
+ input.showAIButton, input.disableEdits, input.expandRoot);
135
+ output.elementsTreeOutline = elementsTreeOutline;
136
+ elementsTreeOutline.addEventListener(ElementsTreeOutline.Events.SelectedNodeChanged, input.onSelectedNodeChanged,
137
+ this);
138
+ elementsTreeOutline.addEventListener(ElementsTreeOutline.Events.ElementsTreeUpdated, input.onElementsTreeUpdated,
139
+ this);
140
+ elementsTreeOutline.addEventListener(UI.TreeOutline.Events.ElementExpanded, input.onElementExpanded, this);
141
+ elementsTreeOutline.addEventListener(UI.TreeOutline.Events.ElementCollapsed, input.onElementCollapsed, this);
142
+ elementsTreeOutline.addEventListener(ElementsTreeOutline.Events.ShowAllRows, () => {
143
+ if (elementsTreeOutline.maxRowsShown) {
145
144
  // Set max to undefined to show all rows
146
- output.elementsTreeOutline.maxRowsShown = undefined;
145
+ elementsTreeOutline.maxRowsShown = undefined;
147
146
  }
148
147
  }, this);
149
- target.appendChild(output.elementsTreeOutline.element);
148
+ elementsTreeOutline.elementInternal.addEventListener('contextmenu', (event: MouseEvent) => {
149
+ const treeElement = elementsTreeOutline.treeElementFromEventInternal(event);
150
+ if (treeElement instanceof ElementsTreeElement) {
151
+ output.elementsTreeOutline?.showContextMenu(treeElement, event);
152
+ }
153
+ }, false);
154
+ output.imagePreviewPopover = new ImagePreviewPopover(
155
+ elementsTreeOutline.contentElement,
156
+ event => {
157
+ let link: (Element|null) = (event.target as Element | null);
158
+ while (link && !ImagePreviewPopover.getImageURL(link)) {
159
+ link = link.parentElementOrShadowHost();
160
+ }
161
+ return link;
162
+ },
163
+ async link => {
164
+ const listItem = UI.UIUtils.enclosingNodeOrSelfWithNodeName(link, 'li');
165
+ if (!listItem) {
166
+ return undefined;
167
+ }
168
+
169
+ const treeElement =
170
+ (UI.TreeOutline.TreeElement.getTreeElementBylistItemNode(listItem) as ElementsTreeElement | undefined);
171
+ return await UIComponentUtils.ImagePreview.loadPrecomputedFeatures(treeElement?.node());
172
+ });
173
+ // TODO(changhaohan): refactor the popover to use tooltip component.
174
+ const popupHelper = new UI.PopoverHelper.PopoverHelper(elementsTreeOutline.elementInternal, event => {
175
+ const hoveredNode = event.composedPath()[0];
176
+ if (!(hoveredNode instanceof Element) || !hoveredNode.matches('.violating-element')) {
177
+ return null;
178
+ }
179
+
180
+ const issues = elementsTreeOutline.issuesByNodeElement(hoveredNode);
181
+ if (!issues) {
182
+ return null;
183
+ }
184
+
185
+ return {
186
+ box: hoveredNode.boxInWindow(),
187
+ show: async (popover: UI.GlassPane.GlassPane) => {
188
+ popover.setIgnoreLeftMargin(true);
189
+ // clang-format off
190
+ render(html`
191
+ <div class="squiggles-content">
192
+ ${issues.map(issue => {
193
+ const elementIssueDetails = getElementIssueDetails(issue);
194
+ if (!elementIssueDetails) {
195
+ // This shouldn't happen, but add this if check to pass ts check.
196
+ return nothing;
197
+ }
198
+ const issueKindIconName = IssueCounter.IssueCounter.getIssueKindIconName(issue.getKind());
199
+ const openIssueEvent = (): Promise<void> => Common.Revealer.reveal(issue);
200
+ return html`
201
+ <div class="squiggles-content-item">
202
+ <devtools-icon .name=${issueKindIconName} @click=${openIssueEvent}></devtools-icon>
203
+ <devtools-link class="link" @click=${openIssueEvent}>${i18nString(UIStrings.viewIssue)}</devtools-link>
204
+ <span>${elementIssueDetails.tooltip}</span>
205
+ </div>`;
206
+ })}
207
+ </div>`, popover.contentElement);
208
+ // clang-format on
209
+ return true;
210
+ },
211
+ };
212
+ }, 'elements.issue');
213
+ popupHelper.setTimeout(300);
214
+ target.appendChild(elementsTreeOutline.element);
150
215
  }
151
216
 
152
217
  output.elementsTreeOutline.maxTreeDepth = input.maxTreeDepth;
153
218
  output.elementsTreeOutline.enableContextMenu = input.enableContextMenu ?? true;
154
- output.elementsTreeOutline.showComments = input.showComments ?? true;
219
+ output.elementsTreeOutline.showContextMenu = (treeElement, event) => {
220
+ void showContextMenu(treeElement, event, input.onSaveNodeToTempVariable);
221
+ };
222
+ let needsUpdate = false;
223
+ const showComments = input.showComments ?? true;
224
+ if (output.elementsTreeOutline.showComments !== showComments) {
225
+ output.elementsTreeOutline.showComments = showComments;
226
+ needsUpdate = true;
227
+ }
155
228
  output.elementsTreeOutline.showAIButton = input.showAIButton ?? true;
156
229
  output.elementsTreeOutline.disableEdits = input.disableEdits ?? false;
157
230
  output.elementsTreeOutline.expandRoot = input.expandRoot ?? false;
@@ -161,6 +234,9 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
161
234
  }
162
235
  if (input.visible !== undefined) {
163
236
  output.elementsTreeOutline.setVisible(input.visible);
237
+ if (!input.visible) {
238
+ output.imagePreviewPopover?.hide();
239
+ }
164
240
  }
165
241
  output.elementsTreeOutline.maxRowsShown = input.maxRowsShown;
166
242
  output.elementsTreeOutline.setWordWrap(input.wrap);
@@ -168,6 +244,9 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
168
244
  if (input.deindentSingleNode) {
169
245
  output.elementsTreeOutline.deindentSingleNode();
170
246
  }
247
+ if (needsUpdate) {
248
+ output.elementsTreeOutline.update();
249
+ }
171
250
  // Node highlighting logic. FIXME: express as a lit template.
172
251
  const previousHighlightedNode = output.highlightedTreeElement?.node() ?? null;
173
252
  if (previousHighlightedNode !== input.currentHighlightedNode) {
@@ -238,7 +317,8 @@ export class DOMTreeWidget extends UI.Widget.Widget {
238
317
 
239
318
  #maxTreeDepth?: number;
240
319
  #enableContextMenu = true;
241
- #showComments = true;
320
+ #showHTMLCommentsSetting = Common.Settings.Settings.instance().moduleSetting('show-html-comments');
321
+ #showComments = this.#showHTMLCommentsSetting.get();
242
322
  #showAIButton = true;
243
323
  #disableEdits = false;
244
324
  #expandRoot = false;
@@ -346,6 +426,7 @@ export class DOMTreeWidget extends UI.Widget.Widget {
346
426
  delegatesFocus: false,
347
427
  });
348
428
  this.#view = view ?? DEFAULT_VIEW;
429
+ this.#showHTMLCommentsSetting.addChangeListener(this.#onShowHTMLCommentsChange, this);
349
430
  if (Common.Settings.Settings.instance().moduleSetting('highlight-node-on-hover-in-overlay').get()) {
350
431
  SDK.TargetManager.TargetManager.instance().addModelListener(
351
432
  SDK.OverlayModel.OverlayModel, SDK.OverlayModel.Events.HIGHLIGHT_NODE_REQUESTED, this.#highlightNode, this,
@@ -356,6 +437,15 @@ export class DOMTreeWidget extends UI.Widget.Widget {
356
437
  }
357
438
  }
358
439
 
440
+ #onShowHTMLCommentsChange(): void {
441
+ this.#showComments = this.#showHTMLCommentsSetting.get();
442
+ const selectedNode = this.selectedDOMNode();
443
+ if (selectedNode && selectedNode.nodeType() === Node.COMMENT_NODE && !this.#showComments) {
444
+ this.selectDOMNode(selectedNode.parentNode);
445
+ }
446
+ this.performUpdate();
447
+ }
448
+
359
449
  #highlightNode(event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode>): void {
360
450
  void this.#highlightThrottler.schedule(() => {
361
451
  this.#currentHighlightedNode = event.data;
@@ -425,40 +515,42 @@ export class DOMTreeWidget extends UI.Widget.Widget {
425
515
 
426
516
  override performUpdate(): void {
427
517
  const firstRender = !this.#viewOutput.elementsTreeOutline;
428
- this.#view(
429
- {
430
- omitRootDOMNode: this.omitRootDOMNode,
431
- selectEnabled: this.selectEnabled,
432
- hideGutter: this.hideGutter,
433
- maxTreeDepth: this.#maxTreeDepth,
434
- enableContextMenu: this.#enableContextMenu,
435
- showComments: this.#showComments,
436
- showAIButton: this.#showAIButton,
437
- disableEdits: this.#disableEdits,
438
- expandRoot: this.#expandRoot,
439
- visibleWidth: this.#visibleWidth,
440
- visible: this.#visible,
441
- wrap: this.#wrap,
442
- maxRowsShown: this.#maxRows,
443
- showSelectionOnKeyboardFocus: this.showSelectionOnKeyboardFocus,
444
- preventTabOrder: this.preventTabOrder,
445
- deindentSingleNode: this.deindentSingleNode,
446
-
447
- currentHighlightedNode: this.#currentHighlightedNode,
448
- selectedNode: this.selectedDOMNode(),
449
- onElementsTreeUpdated: this.onElementsTreeUpdated.bind(this),
450
- onSelectedNodeChanged: event => {
451
- this.#clearHighlightedNode();
452
- this.onSelectedNodeChanged(event);
453
- },
454
- onElementCollapsed: () => {
455
- this.#clearHighlightedNode();
456
- },
457
- onElementExpanded: () => {
458
- this.#clearHighlightedNode();
459
- },
460
- },
461
- this.#viewOutput, this.contentElement);
518
+ this.#view({
519
+ omitRootDOMNode: this.omitRootDOMNode,
520
+ selectEnabled: this.selectEnabled,
521
+ hideGutter: this.hideGutter,
522
+ maxTreeDepth: this.#maxTreeDepth,
523
+ enableContextMenu: this.#enableContextMenu,
524
+ showComments: this.#showComments,
525
+ showAIButton: this.#showAIButton,
526
+ disableEdits: this.#disableEdits,
527
+ expandRoot: this.#expandRoot,
528
+ visibleWidth: this.#visibleWidth,
529
+ visible: this.#visible,
530
+ wrap: this.#wrap,
531
+ maxRowsShown: this.#maxRows,
532
+ showSelectionOnKeyboardFocus: this.showSelectionOnKeyboardFocus,
533
+ preventTabOrder: this.preventTabOrder,
534
+ deindentSingleNode: this.deindentSingleNode,
535
+
536
+ currentHighlightedNode: this.#currentHighlightedNode,
537
+ selectedNode: this.selectedDOMNode(),
538
+ onElementsTreeUpdated: this.onElementsTreeUpdated.bind(this),
539
+ onSelectedNodeChanged: event => {
540
+ this.#clearHighlightedNode();
541
+ this.onSelectedNodeChanged(event);
542
+ },
543
+ onElementCollapsed: () => {
544
+ this.#clearHighlightedNode();
545
+ },
546
+ onElementExpanded: () => {
547
+ this.#clearHighlightedNode();
548
+ },
549
+ onSaveNodeToTempVariable: node => {
550
+ void this.saveNodeToTempVariable(node);
551
+ },
552
+ },
553
+ this.#viewOutput, this.contentElement);
462
554
  if (firstRender && this.#viewOutput.elementsTreeOutline) {
463
555
  this.#viewOutput.elementsTreeOutline.addEventListener(ElementsTreeOutline.Events.ShowAllRows, () => {
464
556
  this.maxRows = undefined;
@@ -529,7 +621,11 @@ export class DOMTreeWidget extends UI.Widget.Widget {
529
621
  }
530
622
 
531
623
  toggleHideElement(node: SDK.DOMModel.DOMNode): void {
532
- void this.#viewOutput.elementsTreeOutline?.toggleHideElement(node);
624
+ void node.toggleHideElement();
625
+ }
626
+
627
+ isToggledToHidden(node: SDK.DOMModel.DOMNode): boolean {
628
+ return node.isToggledToHidden();
533
629
  }
534
630
 
535
631
  toggleEditAsHTML(node: SDK.DOMModel.DOMNode): void {
@@ -537,13 +633,17 @@ export class DOMTreeWidget extends UI.Widget.Widget {
537
633
  }
538
634
 
539
635
  duplicateNode(node: SDK.DOMModel.DOMNode): void {
540
- this.#viewOutput.elementsTreeOutline?.duplicateNode(node);
636
+ node.duplicate();
541
637
  }
542
638
 
543
639
  copyStyles(node: SDK.DOMModel.DOMNode): void {
544
640
  void this.#viewOutput.elementsTreeOutline?.findTreeElement(node)?.copyStyles();
545
641
  }
546
642
 
643
+ async saveNodeToTempVariable(node: SDK.DOMModel.DOMNode): Promise<void> {
644
+ await this.#viewOutput.elementsTreeOutline?.saveNodeToTempVariable(node);
645
+ }
646
+
547
647
  /**
548
648
  * FIXME: used to determine focus state, probably we can have a better
549
649
  * way to do it.
@@ -563,9 +663,18 @@ export class DOMTreeWidget extends UI.Widget.Widget {
563
663
  this.performUpdate();
564
664
  }
565
665
 
666
+ override wasHidden(): void {
667
+ super.wasHidden();
668
+ this.#visible = false;
669
+ this.#viewOutput.imagePreviewPopover?.hide();
670
+ this.performUpdate();
671
+ }
672
+
566
673
  override detach(overrideHideOnDetach?: boolean): void {
567
674
  super.detach(overrideHideOnDetach);
568
675
  this.#visible = false;
676
+ this.#showHTMLCommentsSetting.removeChangeListener(this.#onShowHTMLCommentsChange, this);
677
+ this.#viewOutput.imagePreviewPopover?.hide();
569
678
  this.performUpdate();
570
679
  }
571
680
 
@@ -600,11 +709,9 @@ export class ElementsTreeOutline extends
600
709
  private rootDOMNodeInternal: SDK.DOMModel.DOMNode|null;
601
710
  selectedDOMNodeInternal: SDK.DOMModel.DOMNode|null;
602
711
  private visible: boolean;
603
- private readonly imagePreviewPopover: ImagePreviewPopover;
604
712
  private updateRecords: Map<SDK.DOMModel.DOMNode, Elements.ElementUpdateRecord.ElementUpdateRecord>;
605
713
  private treeElementsBeingUpdated: Set<ElementsTreeElement>;
606
714
  decoratorExtensions: MarkerDecoratorRegistration[]|null;
607
- private showHTMLCommentsSetting: Common.Settings.Setting<boolean>;
608
715
  private multilineEditing?: MultilineEditorController|null;
609
716
  private visibleWidthInternal?: number;
610
717
  private clipboardNodeData?: ClipboardData;
@@ -616,7 +723,6 @@ export class ElementsTreeOutline extends
616
723
  private updateModifiedNodesTimeout?: number;
617
724
  #topLayerContainerByDocument = new WeakMap<SDK.DOMModel.DOMDocument, TopLayerContainer>();
618
725
  #issuesManager?: IssuesManager.IssuesManager.IssuesManager;
619
- #popupHelper?: UI.PopoverHelper.PopoverHelper;
620
726
  #nodeElementToIssues = new Map<Element, IssuesManager.Issue.Issue[]>();
621
727
  maxTreeDepth?: number;
622
728
  enableContextMenu: boolean;
@@ -671,9 +777,6 @@ export class ElementsTreeOutline extends
671
777
  this.elementInternal.addEventListener('clipboard-cut', this.onCopyOrCut.bind(this, true), false);
672
778
  this.elementInternal.addEventListener('clipboard-paste', this.onPaste.bind(this), false);
673
779
  }
674
- if (this.enableContextMenu) {
675
- this.elementInternal.addEventListener('contextmenu', this.contextMenuEventFired.bind(this), false);
676
- }
677
780
 
678
781
  outlineDisclosureElement.appendChild(this.elementInternal);
679
782
  this.element = shadowContainer;
@@ -686,82 +789,11 @@ export class ElementsTreeOutline extends
686
789
 
687
790
  this.visible = false;
688
791
 
689
- this.imagePreviewPopover = new ImagePreviewPopover(
690
- this.contentElement,
691
- event => {
692
- let link: (Element|null) = (event.target as Element | null);
693
- while (link && !ImagePreviewPopover.getImageURL(link)) {
694
- link = link.parentElementOrShadowHost();
695
- }
696
- return link;
697
- },
698
- async link => {
699
- const listItem = UI.UIUtils.enclosingNodeOrSelfWithNodeName(link, 'li');
700
- if (!listItem) {
701
- return undefined;
702
- }
703
-
704
- const treeElement =
705
- (UI.TreeOutline.TreeElement.getTreeElementBylistItemNode(listItem) as ElementsTreeElement | undefined);
706
- return await UIComponentUtils.ImagePreview.loadPrecomputedFeatures(treeElement?.node());
707
- });
708
-
709
792
  this.updateRecords = new Map();
710
793
  this.treeElementsBeingUpdated = new Set();
711
794
 
712
795
  this.decoratorExtensions = null;
713
- if (this.showComments) {
714
- this.showHTMLCommentsSetting = Common.Settings.Settings.instance().moduleSetting('show-html-comments');
715
- this.showHTMLCommentsSetting.addChangeListener(this.onShowHTMLCommentsChange.bind(this));
716
- } else {
717
- this.showHTMLCommentsSetting = {
718
- get: () => false,
719
- addChangeListener: () => {},
720
- removeChangeListener: () => {},
721
- } as unknown as Common.Settings.Setting<boolean>;
722
- }
723
796
  this.setUseLightSelectionColor(true);
724
- // TODO(changhaohan): refactor the popover to use tooltip component.
725
- this.#popupHelper = new UI.PopoverHelper.PopoverHelper(this.elementInternal, event => {
726
- const hoveredNode = event.composedPath()[0] as Element;
727
- if (!hoveredNode?.matches('.violating-element')) {
728
- return null;
729
- }
730
-
731
- const issues = this.#nodeElementToIssues.get(hoveredNode);
732
- if (!issues) {
733
- return null;
734
- }
735
-
736
- return {
737
- box: hoveredNode.boxInWindow(),
738
- show: async (popover: UI.GlassPane.GlassPane) => {
739
- popover.setIgnoreLeftMargin(true);
740
- // clang-format off
741
- render(html`
742
- <div class="squiggles-content">
743
- ${issues.map(issue => {
744
- const elementIssueDetails = getElementIssueDetails(issue);
745
- if (!elementIssueDetails) {
746
- // This shouldn't happen, but add this if check to pass ts check.
747
- return nothing;
748
- }
749
- const issueKindIconName = IssueCounter.IssueCounter.getIssueKindIconName(issue.getKind());
750
- const openIssueEvent = (): Promise<void> => Common.Revealer.reveal(issue);
751
- return html`
752
- <div class="squiggles-content-item">
753
- <devtools-icon .name=${issueKindIconName} @click=${openIssueEvent}></devtools-icon>
754
- <devtools-link class="link" @click=${openIssueEvent}>${i18nString(UIStrings.viewIssue)}</devtools-link>
755
- <span>${elementIssueDetails.tooltip}</span>
756
- </div>`;
757
- })}
758
- </div>`, popover.contentElement);
759
- // clang-format on
760
- return true;
761
- },
762
- };
763
- }, 'elements.issue');
764
- this.#popupHelper.setTimeout(300);
765
797
  }
766
798
 
767
799
  static forDOMModel(domModel: SDK.DOMModel.DOMModel): ElementsTreeOutline|null {
@@ -863,12 +895,8 @@ export class ElementsTreeOutline extends
863
895
  this.#nodeElementToIssues.set(element, issues);
864
896
  }
865
897
 
866
- private onShowHTMLCommentsChange(): void {
867
- const selectedNode = this.selectedDOMNode();
868
- if (selectedNode && selectedNode.nodeType() === Node.COMMENT_NODE && !this.showHTMLCommentsSetting.get()) {
869
- this.selectDOMNode(selectedNode.parentNode);
870
- }
871
- this.update();
898
+ issuesByNodeElement(element: Element): IssuesManager.Issue.Issue[]|undefined {
899
+ return this.#nodeElementToIssues.get(element);
872
900
  }
873
901
 
874
902
  setWordWrap(wrap: boolean): void {
@@ -993,7 +1021,7 @@ export class ElementsTreeOutline extends
993
1021
  }
994
1022
 
995
1023
  duplicateNode(targetNode: SDK.DOMModel.DOMNode): void {
996
- this.performDuplicate(targetNode);
1024
+ targetNode.duplicate();
997
1025
  }
998
1026
 
999
1027
  private onPaste(event: Event): void {
@@ -1031,26 +1059,12 @@ export class ElementsTreeOutline extends
1031
1059
  }
1032
1060
  }
1033
1061
 
1034
- private performDuplicate(targetNode: SDK.DOMModel.DOMNode): void {
1035
- if (targetNode.isInShadowTree()) {
1036
- return;
1037
- }
1038
-
1039
- const parentNode = targetNode.parentNode ? targetNode.parentNode : targetNode;
1040
- if (parentNode.nodeName() === '#document') {
1041
- return;
1042
- }
1043
-
1044
- targetNode.copyTo(parentNode, targetNode.nextSibling);
1045
- }
1046
-
1047
1062
  setVisible(visible: boolean): void {
1048
1063
  if (visible === this.visible) {
1049
1064
  return;
1050
1065
  }
1051
1066
  this.visible = visible;
1052
1067
  if (!this.visible) {
1053
- this.imagePreviewPopover.hide();
1054
1068
  if (this.multilineEditing) {
1055
1069
  this.multilineEditing.cancel();
1056
1070
  }
@@ -1534,60 +1548,9 @@ export class ElementsTreeOutline extends
1534
1548
  }
1535
1549
  }
1536
1550
 
1537
- private contextMenuEventFired(event: MouseEvent): void {
1538
- const treeElement = this.treeElementFromEventInternal(event);
1539
- if (treeElement instanceof ElementsTreeElement) {
1540
- void this.showContextMenu(treeElement, event);
1541
- }
1542
- }
1543
-
1544
- async showContextMenu(treeElement: ElementsTreeElement, event: Event): Promise<void> {
1545
- if (UI.UIUtils.isEditing() || !this.enableContextMenu) {
1546
- return;
1547
- }
1548
-
1549
- const node = (event.target as Node | null);
1550
- if (!node) {
1551
- return;
1552
- }
1553
-
1554
- // The context menu construction may be async. In order to
1555
- // make sure that no other (default) context menu shows up, we need
1556
- // to stop propagating and prevent the default action.
1557
- event.stopPropagation();
1558
- event.preventDefault();
1559
-
1560
- const contextMenu = new UI.ContextMenu.ContextMenu(event);
1561
- const isPseudoElement = Boolean(treeElement.node().pseudoType());
1562
- const isTag = treeElement.node().nodeType() === Node.ELEMENT_NODE && !isPseudoElement;
1563
-
1564
- let textNode: Element|null = node.enclosingNodeOrSelfWithClass('webkit-html-text-node');
1565
- if (textNode?.classList.contains('bogus')) {
1566
- textNode = null;
1567
- }
1568
- const commentNode = node.enclosingNodeOrSelfWithClass('webkit-html-comment');
1569
- contextMenu.saveSection().appendItem(
1570
- i18nString(UIStrings.storeAsGlobalVariable), this.saveNodeToTempVariable.bind(this, treeElement.node()),
1571
- {jslogContext: 'store-as-global-variable'});
1572
- if (textNode) {
1573
- await treeElement.populateTextContextMenu(contextMenu, textNode);
1574
- } else if (isTag) {
1575
- await treeElement.populateTagContextMenu(contextMenu, event);
1576
- } else if (commentNode) {
1577
- await treeElement.populateNodeContextMenu(contextMenu);
1578
- } else if (isPseudoElement) {
1579
- treeElement.populatePseudoElementContextMenu(contextMenu);
1580
- } else if (treeElement.node().nodeType() === Node.PROCESSING_INSTRUCTION_NODE) {
1581
- await treeElement.populateProcessingElementContextMenu(contextMenu);
1582
- }
1583
-
1584
- ElementsPanel.instance().populateAdornerSettingsContextMenu(contextMenu);
1551
+ showContextMenu: (treeElement: ElementsTreeElement, event: Event) => void = () => {};
1585
1552
 
1586
- contextMenu.appendApplicableItems(treeElement.node());
1587
- void contextMenu.show();
1588
- }
1589
-
1590
- private async saveNodeToTempVariable(node: SDK.DOMModel.DOMNode): Promise<void> {
1553
+ async saveNodeToTempVariable(node: SDK.DOMModel.DOMNode): Promise<void> {
1591
1554
  const remoteObjectForConsole = await node.resolveToObject();
1592
1555
  const consoleModel = remoteObjectForConsole?.runtimeModel().target()?.model(SDK.ConsoleModel.ConsoleModel);
1593
1556
  await consoleModel?.saveToTempVariable(
@@ -1701,94 +1664,17 @@ export class ElementsTreeOutline extends
1701
1664
  return newTreeItem;
1702
1665
  }
1703
1666
 
1704
- /**
1705
- * Runs a script on the node's remote object that toggles a class name on
1706
- * the node and injects a stylesheet into the head of the node's document
1707
- * containing a rule to set "visibility: hidden" on the class and all it's
1708
- * ancestors.
1709
- */
1710
1667
  async toggleHideElement(node: SDK.DOMModel.DOMNode): Promise<void> {
1711
- let pseudoElementName = node.pseudoType() ? node.nodeName() : null;
1712
- if (pseudoElementName && node.pseudoIdentifier()) {
1713
- pseudoElementName += `(${node.pseudoIdentifier()})`;
1714
- }
1715
-
1716
- let effectiveNode: SDK.DOMModel.DOMNode|null = node;
1717
- while (effectiveNode?.pseudoType()) {
1718
- if (effectiveNode !== node && effectiveNode.pseudoType() === 'column') {
1719
- // Ideally we would select the specific column pseudo element, but
1720
- // we don't have a way to do that at the moment.
1721
- pseudoElementName = '::column' + pseudoElementName;
1722
- }
1723
- effectiveNode = effectiveNode.parentNode;
1724
- }
1725
- if (!effectiveNode) {
1726
- return;
1727
- }
1728
-
1729
- const hidden = node.marker('hidden-marker');
1730
- const object = await effectiveNode.resolveToObject('');
1731
-
1732
- if (!object) {
1733
- return;
1734
- }
1735
-
1736
- await object.callFunction(
1737
- (toggleClassAndInjectStyleRule as (this: Object, ...arg1: unknown[]) => void),
1738
- [{value: pseudoElementName}, {value: !hidden}]);
1739
- object.release();
1740
- node.setMarker('hidden-marker', hidden ? null : true);
1741
-
1742
- function toggleClassAndInjectStyleRule(this: Element, pseudoElementName: string|null, hidden: boolean): void {
1743
- const classNamePrefix = '__web-inspector-hide';
1744
- const classNameSuffix = '-shortcut__';
1745
- const styleTagId = '__web-inspector-hide-shortcut-style__';
1746
- const pseudoElementNameEscaped = pseudoElementName ? pseudoElementName.replace(/[\(\)\:]/g, '_') : '';
1747
- const className = classNamePrefix + pseudoElementNameEscaped + classNameSuffix;
1748
- this.classList.toggle(className, hidden);
1749
-
1750
- let localRoot: Element|HTMLHeadElement = this;
1751
- while (localRoot.parentNode) {
1752
- localRoot = (localRoot.parentNode as Element);
1753
- }
1754
- if (localRoot.nodeType === Node.DOCUMENT_NODE) {
1755
- localRoot = document.head;
1756
- }
1757
-
1758
- let style = localRoot.querySelector('style#' + styleTagId);
1759
- if (!style) {
1760
- const selectors = [];
1761
- selectors.push('.__web-inspector-hide-shortcut__');
1762
- selectors.push('.__web-inspector-hide-shortcut__ *');
1763
- const selector = selectors.join(', ');
1764
- const ruleBody = ' visibility: hidden !important;';
1765
- const rule = '\n' + selector + '\n{\n' + ruleBody + '\n}\n';
1766
-
1767
- style = document.createElement('style');
1768
- style.id = styleTagId;
1769
- style.textContent = rule;
1770
-
1771
- localRoot.appendChild(style);
1772
- }
1773
-
1774
- // In addition to putting them on the element we want to hide, we will
1775
- // also add pseudo element classes to the style element to keep track of
1776
- // which pseudo elements we have style rules for.
1777
- if (pseudoElementName && !style.classList.contains(className)) {
1778
- style.classList.add(className);
1779
- style.textContent = `.${className}${pseudoElementName}, ${style.textContent}`;
1780
- }
1781
- }
1668
+ await node.toggleHideElement();
1782
1669
  }
1783
1670
 
1784
1671
  isToggledToHidden(node: SDK.DOMModel.DOMNode): boolean {
1785
- return Boolean(node.marker('hidden-marker'));
1672
+ return node.isToggledToHidden();
1786
1673
  }
1787
1674
 
1788
1675
  private reset(): void {
1789
1676
  this.rootDOMNode = null;
1790
1677
  this.selectDOMNode(null, false);
1791
- this.imagePreviewPopover.hide();
1792
1678
  delete this.clipboardNodeData;
1793
1679
  SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight(SDK.TargetManager.TargetManager.instance());
1794
1680
  this.updateRecords.clear();
@@ -2117,7 +2003,7 @@ export class ElementsTreeOutline extends
2117
2003
  if (node.childNodeCount()) {
2118
2004
  // Children may be stale when the outline is not wired to receive DOMModel updates.
2119
2005
  let children: SDK.DOMModel.DOMNode[] = node.children() || [];
2120
- if (!this.showHTMLCommentsSetting.get()) {
2006
+ if (!this.showComments) {
2121
2007
  children = children.filter(n => n.nodeType() !== Node.COMMENT_NODE);
2122
2008
  }
2123
2009
  visibleChildren.push(...children);
@@ -1562,7 +1562,8 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1562
1562
  private createRenderingShortcuts(): UI.Toolbar.ToolbarButton {
1563
1563
  const prefersColorSchemeSetting = Common.Settings.Settings.instance().resolve(
1564
1564
  SDK.SDKSettings.emulatedCSSMediaFeaturePrefersColorSchemeSettingDescriptor);
1565
- const autoDarkModeSetting = Common.Settings.Settings.instance().moduleSetting('emulate-auto-dark-mode');
1565
+ const autoDarkModeSetting =
1566
+ Common.Settings.Settings.instance().resolve(SDK.SDKSettings.emulateAutoDarkModeSettingDescriptor);
1566
1567
  const decorateStatus = (condition: boolean, title: string): string => `${condition ? '✓ ' : ''}${title}`;
1567
1568
 
1568
1569
  const button = new UI.Toolbar.ToolbarToggle(