You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/Deep Dive/DOM.md
+143Lines changed: 143 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -47,3 +47,146 @@ Because JavaScript can [open a new window](https://developer.mozilla.org/en-US/d
47
47
under user gestures and have [access back to its opener](https://developer.mozilla.org/en-US/docs/Web/API/Window/opener),
48
48
multiple web pages across multiple tabs might be able to communicate with one another via JavaScript API
49
49
such as [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage).
50
+
51
+
## Node’s Type and State flags
52
+
53
+
Each node has a set of [`TypeFlag`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/Node.h#L596),
54
+
which are set at construction time and immutable, and a set of [`StateFlag`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/Node.h#L617),
55
+
which can be set or unset throughout [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)’s lifetime.
56
+
Node also makes use of [`EventTargetFlag`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/EventTarget.h#L188)
57
+
for indicating ownership and relationship with other objects.
58
+
For example, [`TypeFlag::IsElement`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/Node.h#L600C9-L600C18)
59
+
is set whenever a [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)
60
+
is a subclass of [`Element`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Element.h).
is set whenever a [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h) is [connected](https://dom.spec.whatwg.org/#connected).
66
+
These flags are updated by each subclass of [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h) throughout its lifetime.
67
+
Note that these flags are set or unset within a specific function.
68
+
For example, [`EventTargetFlag::IsConnected`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/EventTarget.h#L193)
69
+
is set in [`Node::insertedIntoAncestor`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/Node.cpp#L1474).
70
+
It means that any code which runs prior to [`Node::insertedIntoAncestor`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/Node.cpp#L1474)
71
+
running on a given `Node` will observe an outdated value of
In order to construct a [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) tree,
77
+
we create a DOM [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)
78
+
and [insert](https://github.com/WebKit/WebKit/blob/0a9ebe9a13e511c2848b7ed3dfd887be266d42bb/Source/WebCore/dom/ContainerNode.cpp#L279)
79
+
it into a [`ContainerNode`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/ContainerNode.h)
80
+
such as [`Document`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Document.h)
81
+
and [`Element`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Element.cpp).
82
+
An insertion of a node starts with a [validation](https://github.com/WebKit/WebKit/blob/0a9ebe9a13e511c2848b7ed3dfd887be266d42bb/Source/WebCore/dom/ContainerNode.cpp#L477),
83
+
then [removal of the node from its old parent](https://github.com/WebKit/WebKit/blob/0a9ebe9a13e511c2848b7ed3dfd887be266d42bb/Source/WebCore/dom/ContainerNode.cpp#L329)
84
+
if there is any. Either of these two steps can synchronously execute JavaScript via [mutation events](https://developer.mozilla.org/en-US/docs/Web/API/MutationEvent)
85
+
and therefore can synchronously mutate tree’s state.
86
+
Because of that, we need to [check the validity again](https://github.com/WebKit/WebKit/blob/0a9ebe9a13e511c2848b7ed3dfd887be266d42bb/Source/WebCore/dom/ContainerNode.cpp#L866)
87
+
before proceeding with the insertion.
88
+
89
+
An actual insertion of a DOM [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)
90
+
is implemented using [`executeNodeInsertionWithScriptAssertion`](https://github.com/WebKit/WebKit/blob/0a9ebe9a13e511c2848b7ed3dfd887be266d42bb/Source/WebCore/dom/ContainerNode.cpp#L279)
91
+
or [`executeParserNodeInsertionIntoIsolatedTreeWithoutNotifyingParent`](https://github.com/WebKit/WebKit/blob/0a9ebe9a13e511c2848b7ed3dfd887be266d42bb/Source/WebCore/dom/ContainerNode.cpp#L310).
92
+
To start off, these functions instantiate a [RAII](https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization)-style
which forbids JavaScript execution during its lifetime, do the insertion,
95
+
then [notify the child and its descendant](https://github.com/WebKit/WebKit/blob/b7bd89ba227d492f2eeefca628afea8480f556d9/Source/WebCore/dom/ContainerNodeAlgorithms.cpp#L97)
96
+
with [`insertedIntoAncestor`](https://github.com/WebKit/WebKit/blob/b7bd89ba227d492f2eeefca628afea8480f556d9/Source/WebCore/dom/Node.h#L474).
97
+
Note that [`insertedIntoAncestor`](https://github.com/WebKit/WebKit/blob/b7bd89ba227d492f2eeefca628afea8480f556d9/Source/WebCore/dom/Node.h#L474)
98
+
can be called when a given [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)
It’s also not necessarily true that this [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)’s immediate parent node changed.
109
+
It could be this [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)’s ancestor that got inserted into a new parent.
110
+
To run code only when this [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)’s immediate parent had changed,
111
+
check if node’s parent node matches [`parentOfInsertedTree`](https://github.com/WebKit/WebKit/blob/b7bd89ba227d492f2eeefca628afea8480f556d9/Source/WebCore/dom/Node.h#L474).
112
+
There are cases in which code must run whenever its [`TreeScope`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/TreeScope.h)
In all cases, it’s vital that no code invoked by `insertedIntoAncestor` attempts to execute JavaScript synchronously, for example, by dispatching an event.
118
+
Doing so will result in a [release assert](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/bindings/js/ScriptController.cpp#L794) (i.e. crash).
119
+
If an element must dispatch events or otherwise execute arbitrary author JavaScript,
120
+
return [`NeedsPostInsertionCallback`](https://github.com/WebKit/WebKit/blob/b7bd89ba227d492f2eeefca628afea8480f556d9/Source/WebCore/dom/Node.h#L466) from `insertedIntoAncestor`.
121
+
This will result in a call to [`didFinishInsertingNode`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h#L475C18-L475C40)
122
+
which unlike `insertedIntoAncestor` allows script execution (it gets called only after
123
+
[`ScriptDisallowedScope`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/ScriptDisallowedScope.h) has been out of scope).
124
+
But note that the tree’s state may have been mutated by other scripts between when `insertedIntoAncestor` is called and by when `didFinishInsertingNode` is called
125
+
so it’s not safe to assume any tree state condition which was true during `insertedIntoAncestor` to be true in `didFinishInsertingNode`.
126
+
It’s also not safe to leave Node in an inconsistent state at the end of `insertedIntoAncestor`
127
+
because JavaScript may invoke any API on such a Node between `insertedIntoAncestor` and `didFinishInsertingNode`.
128
+
After invoking `insertedIntoAncestor`, these functions invoke
129
+
[`childrenChanged`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/ContainerNode.h#L111) on the new parent.
130
+
This function has the first opportunity to execute any JavaScript in response to a child node being inserted.
for example, may execute its script in [its `childrenChanged`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/ScriptElement.cpp#L92).
133
+
Finally, the functions will invoke [`didFinishInsertingNode`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/Node.h#L475)
134
+
on [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)s which returned
from its `insertedIntoAncestor` and [trigger mutation events](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/ContainerNode.cpp#L1056)
137
+
such as `DOMNodeInsertedEvent`.
138
+
139
+
The removal of a DOM [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h) from its parent is implemented using
and [`ContainerNode::removeChildWithScriptAssertion`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/ContainerNode.cpp#L180).
142
+
These functions first [dispatch mutation events](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/ContainerNode.cpp#L1076)
143
+
and check if child’s parent is still the same container node.
144
+
If it’s not, we stop and exit early. Next, they [disconnect any subframes](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/ContainerNodeAlgorithms.cpp#L263)
145
+
in the subtree to be removed. These functions then instantiate a [RAII](https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization)-style object
which forbids JavaScript execution during its lifetime like the insertion counterparts,
148
+
and [notify](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/Document.cpp#L5828)
149
+
[`Document`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Document.h) of the node’s removal so that objects such as
150
+
[`NodeIterator`](https://developer.mozilla.org/en-US/docs/Web/API/NodeIterator) and [`Range`](https://developer.mozilla.org/en-US/docs/Web/API/Range) can be updated.
151
+
The functions will then do the removal and notify the child and its descendant with
Note that [`removedFromAncestor`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/ContainerNodeAlgorithms.cpp#L177)
154
+
can be called when a given [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)
from a [`Document`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Document.h), or it’s removed from an already disconnected subtree.
157
+
It’s not correct to assume that `this`[`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)
158
+
used to be [connected](https://dom.spec.whatwg.org/#connected) to a [`Document`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Document.h) in `removedFromAncestor`.
159
+
To run code only when a [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)
160
+
[becomes disconnected](https://html.spec.whatwg.org/multipage/infrastructure.html#becomes-disconnected) from a document,
It’s also not necessarily true that this [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)’s immediate parent node changed.
164
+
It could be this [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)’s ancestor that got removed from its old parent.
165
+
To run code only when this [`Node`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/Node.h)’s immediate parent had changed,
166
+
check if node’s parent node is `nullptr`.
167
+
To run code whenever its [`TreeScope`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/TreeScope.h)
attempts to execute JavaScript synchronously, for example, by dispatching an event.
175
+
Doing so will result in a [release assert](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/bindings/js/ScriptController.cpp#L794) (i.e. crash).
176
+
If an element must dispatch events or otherwise execute arbitrary author JavaScript,
177
+
[queue a task](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/EventLoop.h#L206) to do so.
178
+
After invoking `removedFromAncestor`, these functions invoke
179
+
[`childrenChanged`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/ContainerNode.h#L111) on the old parent.
180
+
181
+
Additionally, certain [`StateFlag`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/Node.h#L617) and
might be outdated in `insertedIntoAncestor` and `removedFromAncestor`.
184
+
For example, [`EventTargetFlag::IsConnected`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/EventTarget.h#L193)
185
+
flag is not set or unset until [`Node::insertedIntoAncestor`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/Node.cpp#L1474)
186
+
or [`Node::removedFromAncestor`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/Node.cpp#L1486) is called.
187
+
Accessing other node’s states and member functions are even trickier.
188
+
Because `insertedIntoAncestor` or `removedFromAncestor` may not have been called on such nodes,
189
+
functions like [`getElementById`](https://github.com/WebKit/WebKit/blob/main/Source/WebCore/dom/TreeScope.h#L83)
190
+
and [`rootNode`](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/dom/ContainerNode.h#L209)
191
+
will return wrong results for those nodes.
192
+
Code which runs inside these functions must carefully [avoid these pitfalls](https://github.com/WebKit/WebKit/blob/5ee1e908b6ed778eca6b6a72997648b10d4bcbf4/Source/WebCore/html/FormAssociatedElement.cpp#L62).
0 commit comments