RESOLVED FIXED 92965
Optimize ChildNode{Insertion,Removal}Notifier::notify() by lazily taking a snapshot of child nodes
https://bugs.webkit.org/show_bug.cgi?id=92965
Summary Optimize ChildNode{Insertion,Removal}Notifier::notify() by lazily taking a sn...
Kentaro Hara
Reported 2012-08-02 02:45:42 PDT
notifyDescendantRemovedFromDocument() cannot iterate child nodes in this way: void notifyDescendantRemovedFromDocument(Node* node) { for (Node* child = node->firstChild(); child; child = child->nextSibling()) { ...; notifyNodeRemovedFromDocument(child); } } This is because notifyNodeRemovedFromDocument(child) might dispatch events and the events might change child trees. To avoid security issues, the current code takes a snapshot of child nodes before starting the iteration, like this. void notifyDescendantRemovedFromDocument(Node* node) { NodeVector children; getChildNodes(node, children); // Take a snapshot. for (int i = 0; i < children.size(); i++) { ...; notifyNodeRemovedFromDocument(children[i]); } } Based on the observation that in almost all cases events won't be dispatched from inside notifyNodeRemovedFromDocument(), we can implement a "lazy" snapshot. The snapshot is taken at the point where EventDispatcher::dispatchEvent() is invoked. The snapshot is not taken unless any event is dispatched.
Attachments
Patch (8.67 KB, patch)
2012-08-02 02:50 PDT, Kentaro Hara
no flags
Patch (8.67 KB, patch)
2012-08-02 02:51 PDT, Kentaro Hara
no flags
Patch (8.77 KB, patch)
2012-08-02 04:30 PDT, Kentaro Hara
no flags
Patch (9.49 KB, patch)
2012-08-02 19:58 PDT, Kentaro Hara
no flags
patch for landing (10.01 KB, patch)
2012-08-07 18:08 PDT, Kentaro Hara
no flags
patch for landing (9.54 KB, patch)
2012-08-07 18:09 PDT, Kentaro Hara
no flags
Kentaro Hara
Comment 1 2012-08-02 02:50:06 PDT
Kentaro Hara
Comment 2 2012-08-02 02:51:10 PDT
Kentaro Hara
Comment 3 2012-08-02 04:30:24 PDT
Adam Barth
Comment 4 2012-08-02 08:17:13 PDT
Comment on attachment 156040 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=156040&action=review Nice idea. Some detailed comments below. > Source/WebCore/dom/ContainerNode.h:304 > +static ChildNodesSnapshot* latestSnapshot = 0; Please don't add static global variables to header files. That will add storage for this variable to every complication unit that includes this header! > Source/WebCore/dom/ContainerNode.h:324 > + if (m_childNodes) > + delete m_childNodes; Can we use an OwnPtr to have this delete happen automatically? > Source/WebCore/dom/ContainerNode.h:338 > + Node* ret = (*m_childNodes)[m_currentIndex++].get(); ret -> node (prefer complete words in variable names) > Source/WebCore/dom/ContainerNode.h:344 > + if (!hasSnapshot()) { Prefer early return. > Source/WebCore/dom/ContainerNodeAlgorithms.cpp:69 > + ChildNodesSnapshot snapshot(node); I'd put the word "Lazy" into the name of this class to make it clear that the snapshot is happening lazily.
Kentaro Hara
Comment 5 2012-08-02 19:58:14 PDT
Kentaro Hara
Comment 6 2012-08-02 19:58:57 PDT
(In reply to comment #3) > > Source/WebCore/dom/ContainerNode.h:304 > > +static ChildNodesSnapshot* latestSnapshot = 0; > > Please don't add static global variables to header files. That will add storage for this variable to every complication unit that includes this header! > > > Source/WebCore/dom/ContainerNode.h:324 > > + if (m_childNodes) > > + delete m_childNodes; > > Can we use an OwnPtr to have this delete happen automatically? > > > Source/WebCore/dom/ContainerNode.h:338 > > + Node* ret = (*m_childNodes)[m_currentIndex++].get(); > > ret -> node (prefer complete words in variable names) > > > Source/WebCore/dom/ContainerNode.h:344 > > + if (!hasSnapshot()) { > > Prefer early return. > > > Source/WebCore/dom/ContainerNodeAlgorithms.cpp:69 > > + ChildNodesSnapshot snapshot(node); > > I'd put the word "Lazy" into the name of this class to make it clear that the snapshot is happening lazily. All done. Thanks.
Adam Barth
Comment 7 2012-08-07 17:01:44 PDT
Comment on attachment 156248 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=156248&action=review > Source/WebCore/dom/ContainerNode.h:308 > + ChildNodesLazySnapshot(Node* parentNode) explicit > Source/WebCore/dom/ContainerNode.h:325 > + if (LIKELY(!m_childNodes.get())) { Can we call hasSnapshot() here? That would be slightly more readable.
Kentaro Hara
Comment 8 2012-08-07 18:08:03 PDT
Created attachment 157065 [details] patch for landing
Kentaro Hara
Comment 9 2012-08-07 18:09:35 PDT
Created attachment 157066 [details] patch for landing
WebKit Review Bot
Comment 10 2012-08-07 22:38:51 PDT
Comment on attachment 157066 [details] patch for landing Clearing flags on attachment: 157066 Committed r124990: <http://trac.webkit.org/changeset/124990>
Ojan Vafai
Comment 11 2012-08-08 11:59:36 PDT
Could we do this same trick for the methods in ContainerNode that call collectChildrenAndRemoveFromOldParent and/or collectTargetNodes? It might have a performance benefit for large innerHTML calls for example.
Kentaro Hara
Comment 12 2012-08-08 16:39:03 PDT
(In reply to comment #11) > Could we do this same trick for the methods in ContainerNode that call collectChildrenAndRemoveFromOldParent and/or collectTargetNodes? It might have a performance benefit for large innerHTML calls for example. Yes, I'll do after investigating performance and security stuff.
Note You need to log in before you can comment on or make changes to this bug.