-
Notifications
You must be signed in to change notification settings - Fork 6k
Don't mutate children on DomNode #33818
Don't mutate children on DomNode #33818
Conversation
Don't mutate the children of a DOM node directly.
lib/web_ui/lib/src/engine/dom.dart
Outdated
| DomElement? get parent => js_util.getProperty(this, 'parentElement'); | ||
| String? get text => js_util.getProperty(this, 'textContent'); | ||
| external DomNode? get parentNode; | ||
| external DomNode? get firstChild; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think firstChild / lastChild already exist (lines 145 / 147).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I hadn't merged main yet when I wrote this.
joshualitt
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good, modulo some minor feedback.
| ) { | ||
| int indexInFlutterView = -1; | ||
| if (headClipView.parent != null) { | ||
| indexInFlutterView = skiaSceneHost!.children.indexOf(headClipView); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually safe for now, but nice to get rid of if we decide we want to move off using List for children.
| _svgPathDefs?.children.single.children.forEach(removeElement); | ||
| final DomElement? parent = _svgPathDefs?.children.single; | ||
| if (parent != null) { | ||
| for (DomNode? child = parent.lastChild; child != null; child = parent.lastChild) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We talked offline but:
while (parent.firstChild != null) {
parent.removeChild(parent.firstChild)
}
is a bit more succinct. I'm okay with either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to stick with this because it only calls lastChild once per iteration, and I don't have to do a nullability assertion (It would have to be parent.removeChild(parent.firstChild!))
* flutter/flutter#104972 Don't mutate the children of a DOM node directly. * nextSibling should be nullable. * Remove redundant firstChild/lastChild fields. * Update to parentNode instead of parentElement.
DomNode differs from the dart:html node in that dart:html does some magic under the hood to reflect changes made to the container to the underlying HTML node. DomNode does not do this, and its children are not mutable. Therefore use the normal JavaScript APIs to modify the tree.
Added a unit test that exercises this code path, which is when the chain of clip views changes length. This was the code path that was causing the crash described in the issue. The example in the issue reproduced this easily before these changes, and does not occur after.
Note that as part of updating the web engine to the new Dom API, @joshualitt is possibly planning on changing the type of the
childrenfield to be an immutable type (which would have caught this issue at compile time).Fixes flutter/flutter#104972