Bug 238226

Summary: Web Inspector: Elements tab: selection variable not displayed after losing focus
Product: WebKit Reporter: Jeff Johnson <opendarwin>
Component: Web InspectorAssignee: Patrick Angle <pangle>
Status: RESOLVED FIXED    
Severity: Normal CC: ews-watchlist, hi, inspector-bugzilla-changes, pangle, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: Safari 15   
Hardware: Mac (Intel)   
OS: macOS 11   
Bug Depends on:    
Bug Blocks: 238843    
Attachments:
Description Flags
Screenshot 1
none
Screenshot 2
none
Screenshot 3
none
Patch v1.0
none
Patch v1.1
none
Patch v1.2 none

Description Jeff Johnson 2022-03-22 14:43:10 PDT
Created attachment 455430 [details]
Screenshot 1

Steps to reproduce:
1) Open https://webkit.org in Safari
2) Open web inspector
3) Select Elements tab
4) Select a DOM node
5) Notice the selection variable = $0
6) Switch to the Console tab
7) Switch back to the Elements tab
8) Notice the selection variable is not display (this may be a bug already)
9) Select the node again

Expected results: The selection variable = $0 is displayed.

Actual results: The selection variable is not displayed. See attached screenshots in order, 1,2,3.
Comment 1 Jeff Johnson 2022-03-22 14:43:32 PDT
Created attachment 455431 [details]
Screenshot 2
Comment 2 Jeff Johnson 2022-03-22 14:43:48 PDT
Created attachment 455432 [details]
Screenshot 3
Comment 3 Patrick Angle 2022-03-22 15:19:36 PDT
Created attachment 455437 [details]
Patch v1.0
Comment 4 Devin Rousso 2022-03-22 15:28:39 PDT
Comment on attachment 455437 [details]
Patch v1.0

View in context: https://bugs.webkit.org/attachment.cgi?id=455437&action=review

> Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.js:185
> +                if (node === WI.domManager.inspectedNode)
> +                    treeElement.listItemElement.classList.add("inspected-node");

Do we also need to do this inside `if (this._includeRootDOMNode) {`?

Also, this logic only seems to handle the top-level nodes.  What if the inspected node is nested deeper?

Maybe we should just throw something like this right before the first early-return, as I think by that point we should've fully reconstructed the visible DOM?
```
let inspectedNodeTreeElement = this.findTreeElement(WI.domManager.inspectedNode);
if (inspectedNodeTreeElement) {
    inspectedNodeTreeElement.reveal();
    inspectedNodeTreeElement.listItemElement.classList.add("inspected-node");
}
```
Comment 5 Patrick Angle 2022-03-22 15:31:57 PDT
Comment on attachment 455437 [details]
Patch v1.0

View in context: https://bugs.webkit.org/attachment.cgi?id=455437&action=review

>> Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.js:185
>> +                    treeElement.listItemElement.classList.add("inspected-node");
> 
> Do we also need to do this inside `if (this._includeRootDOMNode) {`?
> 
> Also, this logic only seems to handle the top-level nodes.  What if the inspected node is nested deeper?
> 
> Maybe we should just throw something like this right before the first early-return, as I think by that point we should've fully reconstructed the visible DOM?
> ```
> let inspectedNodeTreeElement = this.findTreeElement(WI.domManager.inspectedNode);
> if (inspectedNodeTreeElement) {
>     inspectedNodeTreeElement.reveal();
>     inspectedNodeTreeElement.listItemElement.classList.add("inspected-node");
> }
> ```

🤦🏻‍♂️ yep.
Comment 6 Patrick Angle 2022-03-22 15:43:48 PDT
Created attachment 455440 [details]
Patch v1.1
Comment 7 Patrick Angle 2022-03-22 15:46:10 PDT
Created attachment 455442 [details]
Patch v1.2
Comment 8 Devin Rousso 2022-03-22 15:58:35 PDT
Comment on attachment 455442 [details]
Patch v1.2

View in context: https://bugs.webkit.org/attachment.cgi?id=455442&action=review

r=me, nice!

... but I did get a bit curious 😅

I think the backtrace for this bug is when `WI.DOMTreeOutline.prototype.update` is called by `WI.DOMTreeOutline.prototype.setVisible`.

Looking at `WI.DOMTreeOutline.prototype.setVisible`, I think there may be some other (possibly previously undiscovered) bugs in that we call `this.update()` _last_, which seems to completely wipe out the entire tree, meaning that
- `this._updateModifiedNodes()` is kinda pointless since we recreate all the `WI.DOMTreeElement`
- `this._revealAndSelectNode(this._selectedDOMNode, omitFocus)` might not end up doing anything since the `WI.DOMTreeElement` that we might reveal and select (assuming `this._selectedDOMNode` is set) is then immediately removed by `this.update()`
We might need to move the `this.update()` down below (and maybe get rid of `this._updateModifiedNodes()`), or do some other restructuring of things.  Not really sure.

Either way, I think what you've done here is probably fine :)

> Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.js:195
> +                inspectedNodeTreeElement.reveal();

NIT: I wonder if we should also do this inside `WI.DOMTreeOutline.prototype._handleInspectedNodeChanged` 🤔
Comment 9 Patrick Angle 2022-03-22 16:12:04 PDT
(In reply to Devin Rousso from comment #8)
> ... but I did get a bit curious 😅
> 
> I think the backtrace for this bug is when
> `WI.DOMTreeOutline.prototype.update` is called by
> `WI.DOMTreeOutline.prototype.setVisible`.
> 
> Looking at `WI.DOMTreeOutline.prototype.setVisible`, I think there may be
> some other (possibly previously undiscovered) bugs in that we call
> `this.update()` _last_, which seems to completely wipe out the entire tree,
> meaning that
> - `this._updateModifiedNodes()` is kinda pointless since we recreate all the
> `WI.DOMTreeElement`
> - `this._revealAndSelectNode(this._selectedDOMNode, omitFocus)` might not
> end up doing anything since the `WI.DOMTreeElement` that we might reveal and
> select (assuming `this._selectedDOMNode` is set) is then immediately removed
> by `this.update()`
> We might need to move the `this.update()` down below (and maybe get rid of
> `this._updateModifiedNodes()`), or do some other restructuring of things. 
> Not really sure.
> 
> Either way, I think what you've done here is probably fine :)

Yeah, so currently `update` will actually remember what is selected before it removes all the items and rebuilds them, and then reselect stuff. Certainly not the most efficient, but I think selection should be correctly maintained during updates due to this. Some deeper work is almost certainly in order to untangle the order of operations here, though.
Comment 10 Patrick Angle 2022-03-22 16:29:31 PDT
Comment on attachment 455442 [details]
Patch v1.2

View in context: https://bugs.webkit.org/attachment.cgi?id=455442&action=review

>> Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.js:195
>> +                inspectedNodeTreeElement.reveal();
> 
> NIT: I wonder if we should also do this inside `WI.DOMTreeOutline.prototype._handleInspectedNodeChanged` 🤔

I don't think it's currently possible for the inspected node to be set (thus causing this handler to be called) without it being visible in the DOM tree to select it in the first place.
Comment 11 Radar WebKit Bug Importer 2022-03-22 16:37:31 PDT
<rdar://problem/90666532>
Comment 12 EWS 2022-03-22 17:20:43 PDT
Committed r291729 (248761@main): <https://commits.webkit.org/248761@main>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 455442 [details].