|
| 1 | +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Alex Rudenko <alexrudenko@chromium.org> |
| 3 | +Date: Thu, 20 Nov 2025 11:18:30 +0100 |
| 4 | +Subject: Fix element tree flickering with node inspection |
| 5 | + |
| 6 | +Regressed in https://crrev.com/c/6888910. The highlighting in the tree |
| 7 | +outline emits the INSPECT_MODE_WILL_BE_TOGGLED on the overlay model |
| 8 | +causing the tree to clear the highlight immediately. This CL |
| 9 | +recovers the logic missed in the https://crrev.com/c/6888910 |
| 10 | +to prevent re-entrancy during highlighting. |
| 11 | + |
| 12 | +Fixed: 462120622 |
| 13 | +Change-Id: I08af098f7a142085c2fb16511031855623e07c4b |
| 14 | +Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7170551 |
| 15 | +Reviewed-by: Philip Pfaffe <pfaffe@chromium.org> |
| 16 | +Commit-Queue: Philip Pfaffe <pfaffe@chromium.org> |
| 17 | +Auto-Submit: Alex Rudenko <alexrudenko@chromium.org> |
| 18 | + |
| 19 | +diff --git a/front_end/panels/elements/DOMTreeWidget.test.ts b/front_end/panels/elements/DOMTreeWidget.test.ts |
| 20 | +index 43ae133087a61c47c84349c7550485bd52b23847..d83fddd7afc1871b8064631d63c1b6b1e5357b70 100644 |
| 21 | +--- a/front_end/panels/elements/DOMTreeWidget.test.ts |
| 22 | ++++ b/front_end/panels/elements/DOMTreeWidget.test.ts |
| 23 | +@@ -24,6 +24,7 @@ describeWithMockConnection('DOMTreeWidget', () => { |
| 24 | + elementsTreeOutline, |
| 25 | + alreadyExpandedParentTreeElement: null, |
| 26 | + highlightedTreeElement: null, |
| 27 | ++ isUpdatingHighlights: false, |
| 28 | + }); |
| 29 | + const domTree = new Elements.ElementsTreeOutline.DOMTreeWidget(undefined, view); |
| 30 | + domTree.performUpdate(); |
| 31 | +diff --git a/front_end/panels/elements/ElementsTreeOutline.ts b/front_end/panels/elements/ElementsTreeOutline.ts |
| 32 | +index bd8093e1c0961648e782234ed5826273d6ace6d9..c6c8d9d29a84f872126ca0b7c847dffe08c285ad 100644 |
| 33 | +--- a/front_end/panels/elements/ElementsTreeOutline.ts |
| 34 | ++++ b/front_end/panels/elements/ElementsTreeOutline.ts |
| 35 | +@@ -105,6 +105,7 @@ interface ViewInput { |
| 36 | + interface ViewOutput { |
| 37 | + elementsTreeOutline?: ElementsTreeOutline; |
| 38 | + highlightedTreeElement: ElementsTreeElement|null; |
| 39 | ++ isUpdatingHighlights: boolean; |
| 40 | + alreadyExpandedParentTreeElement: ElementsTreeElement|null; |
| 41 | + } |
| 42 | + |
| 43 | +@@ -136,6 +137,7 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE |
| 44 | + // Node highlighting logic. FIXME: express as a lit template. |
| 45 | + const previousHighlightedNode = output.highlightedTreeElement?.node() ?? null; |
| 46 | + if (previousHighlightedNode !== input.currentHighlightedNode) { |
| 47 | ++ output.isUpdatingHighlights = true; |
| 48 | + let treeElement: ElementsTreeElement|null = null; |
| 49 | + |
| 50 | + if (output.highlightedTreeElement) { |
| 51 | +@@ -173,6 +175,7 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE |
| 52 | + output.highlightedTreeElement = treeElement; |
| 53 | + output.elementsTreeOutline.setHoverEffect(treeElement); |
| 54 | + treeElement?.reveal(true); |
| 55 | ++ output.isUpdatingHighlights = false; |
| 56 | + } |
| 57 | + }; |
| 58 | + |
| 59 | +@@ -225,6 +228,7 @@ export class DOMTreeWidget extends UI.Widget.Widget { |
| 60 | + #viewOutput: ViewOutput = { |
| 61 | + highlightedTreeElement: null, |
| 62 | + alreadyExpandedParentTreeElement: null, |
| 63 | ++ isUpdatingHighlights: false, |
| 64 | + }; |
| 65 | + #highlightThrottler = new Common.Throttler.Throttler(100); |
| 66 | + |
| 67 | +@@ -239,8 +243,8 @@ export class DOMTreeWidget extends UI.Widget.Widget { |
| 68 | + SDK.OverlayModel.OverlayModel, SDK.OverlayModel.Events.HIGHLIGHT_NODE_REQUESTED, this.#highlightNode, this, |
| 69 | + {scoped: true}); |
| 70 | + SDK.TargetManager.TargetManager.instance().addModelListener( |
| 71 | +- SDK.OverlayModel.OverlayModel, SDK.OverlayModel.Events.INSPECT_MODE_WILL_BE_TOGGLED, this.#clearState, this, |
| 72 | +- {scoped: true}); |
| 73 | ++ SDK.OverlayModel.OverlayModel, SDK.OverlayModel.Events.INSPECT_MODE_WILL_BE_TOGGLED, |
| 74 | ++ this.#clearHighlightedNode, this, {scoped: true}); |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | +@@ -251,7 +255,13 @@ export class DOMTreeWidget extends UI.Widget.Widget { |
| 79 | + }); |
| 80 | + } |
| 81 | + |
| 82 | +- #clearState(): void { |
| 83 | ++ #clearHighlightedNode(): void { |
| 84 | ++ // Highlighting an element via tree outline will emit the |
| 85 | ++ // INSPECT_MODE_WILL_BE_TOGGLED event, therefore, we skip it if the view |
| 86 | ++ // informed us that it is updating the element. |
| 87 | ++ if (this.#viewOutput.isUpdatingHighlights) { |
| 88 | ++ return; |
| 89 | ++ } |
| 90 | + this.#currentHighlightedNode = null; |
| 91 | + this.requestUpdate(); |
| 92 | + } |
| 93 | +@@ -305,11 +315,11 @@ export class DOMTreeWidget extends UI.Widget.Widget { |
| 94 | + currentHighlightedNode: this.#currentHighlightedNode, |
| 95 | + onElementsTreeUpdated: this.onElementsTreeUpdated.bind(this), |
| 96 | + onSelectedNodeChanged: event => { |
| 97 | +- this.#clearState(); |
| 98 | ++ this.#clearHighlightedNode(); |
| 99 | + this.onSelectedNodeChanged(event); |
| 100 | + }, |
| 101 | +- onElementCollapsed: this.#clearState.bind(this), |
| 102 | +- onElementExpanded: this.#clearState.bind(this), |
| 103 | ++ onElementCollapsed: this.#clearHighlightedNode.bind(this), |
| 104 | ++ onElementExpanded: this.#clearHighlightedNode.bind(this), |
| 105 | + }, |
| 106 | + this.#viewOutput, this.contentElement); |
| 107 | + } |