<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[165103] trunk/Source/WebCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/165103">165103</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2014-03-05 02:41:48 -0800 (Wed, 05 Mar 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>appendChild shouldn't invalidate LiveNodeLists and HTMLCollections if they don't have valid caches
https://bugs.webkit.org/show_bug.cgi?id=129727

Reviewed by Andreas Kling.

Before this patch, invalidateNodeListAndCollectionCachesInAncestors invalidated node lists and HTML
collections on ancestors of a node whenever we're inserting or removing a child node. This patch
makes HTMLCollections and LiveNodeLists register themselves with Document only when they have valid
caches.

Each user of CollectionIndexCache now implements willValidateIndexCache member function that gets
called when CollectionIndexCache caches any state and necessitates the registration with document.

* dom/ChildNodeList.h: Added an empty willValidateIndexCache since child node lists are never
registered with document.

* dom/CollectionIndexCache.h:
(WebCore::CollectionIndexCache::hasValidCache): Added.
(WebCore::CollectionIndexCache::nodeCount): Calls willValidateIndexCache when caching node count.
(WebCore::CollectionIndexCache::nodeAfterCached): Ditto. Also assert that hasValidCache() true in
the cases where we're simply updating our caches or adding more caches.
(WebCore::CollectionIndexCache::nodeAt): Ditto. Also added a code to set the length cache when
we've reached the end of the list. This should be a slight speed up on some cases.

* dom/Document.cpp:
(WebCore::Document::Document): Initializes a variable used by assertions.
(WebCore::Document::unregisterNodeList): Added an early exit for when m_listsInvalidatedAtDocument
is empty since invalidateNodeListAndCollectionCaches swaps out the list.
(WebCore::Document::registerCollection): Removed the boolean hasIdNameMap since we now explicitly
call collectionCachedIdNameMap in HTMLCollection.
(WebCore::Document::unregisterCollection): Ditto. Exit early if m_collectionsInvalidatedAtDocument
is empty since invalidateNodeListAndCollectionCaches swaps out the list.
* dom/Document.h:

* dom/LiveNodeList.cpp:
(WebCore::LiveNodeList::invalidateCache): Unregister the node list with document if we had caches.
* dom/LiveNodeList.h:
(WebCore::LiveNodeList::LiveNodeList):
(WebCore::LiveNodeList::~LiveNodeList): Ditto.
(WebCore::LiveNodeList::invalidateCache): Pass around document. This is necessary since document()
had already moved to the new document inside NodeListsNodeData::invalidateCaches.
(WebCore::LiveNodeList::willValidateIndexCache): Added. Registers itself with document.

* dom/Node.cpp:
(WebCore::Document::invalidateNodeListAndCollectionCaches): Swap the lists since invalidateCache
tries to unregister node lists and HTML collections with document. Since this is the only case in
which node lists and HTML collections being removed may not be in the lists in unregisterNodeList
and unregisterCollection, assert this condition via m_inInvalidateNodeListAndCollectionCaches.
(WebCore::NodeListsNodeData::invalidateCaches):

* dom/NodeRareData.h:
(WebCore::NodeListsNodeData::adoptDocument): Unregister node lists and HTML collections from old
document via invalidateCache. We need to explicitly pass in oldDocument here since owner node's
document had already been changed to newDocument at this point. Since we're invalidating caches,
there is no need to register node lists and HTML collections with newDocument.

* html/HTMLCollection.cpp:
(WebCore::HTMLCollection::HTMLCollection):
(WebCore::HTMLCollection::~HTMLCollection): Unregister the node list with document if we had caches.
(WebCore::HTMLCollection::invalidateCache): Ditto.
(WebCore::HTMLCollection::invalidateNamedElementCache):
* html/HTMLCollection.h:
(WebCore::HTMLCollection::invalidateCache): Pass around document as done in LiveNodeList.
(WebCore::HTMLCollection::willValidateIndexCache): Ditto.

* html/HTMLFormControlsCollection.cpp:
(WebCore::HTMLFormControlsCollection::invalidateCache): Ditto.
* html/HTMLFormControlsCollection.h:

* html/HTMLSelectElement.cpp:
(WebCore::HTMLSelectElement::invalidateSelectedItems): Ditto.
(WebCore::HTMLSelectElement::setRecalcListItems): Ditto.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoredomChildNodeListh">trunk/Source/WebCore/dom/ChildNodeList.h</a></li>
<li><a href="#trunkSourceWebCoredomCollectionIndexCacheh">trunk/Source/WebCore/dom/CollectionIndexCache.h</a></li>
<li><a href="#trunkSourceWebCoredomDocumentcpp">trunk/Source/WebCore/dom/Document.cpp</a></li>
<li><a href="#trunkSourceWebCoredomDocumenth">trunk/Source/WebCore/dom/Document.h</a></li>
<li><a href="#trunkSourceWebCoredomLiveNodeListcpp">trunk/Source/WebCore/dom/LiveNodeList.cpp</a></li>
<li><a href="#trunkSourceWebCoredomLiveNodeListh">trunk/Source/WebCore/dom/LiveNodeList.h</a></li>
<li><a href="#trunkSourceWebCoredomNodecpp">trunk/Source/WebCore/dom/Node.cpp</a></li>
<li><a href="#trunkSourceWebCoredomNodeRareDatah">trunk/Source/WebCore/dom/NodeRareData.h</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLCollectioncpp">trunk/Source/WebCore/html/HTMLCollection.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLCollectionh">trunk/Source/WebCore/html/HTMLCollection.h</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLFormControlsCollectioncpp">trunk/Source/WebCore/html/HTMLFormControlsCollection.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLFormControlsCollectionh">trunk/Source/WebCore/html/HTMLFormControlsCollection.h</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLSelectElementcpp">trunk/Source/WebCore/html/HTMLSelectElement.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (165102 => 165103)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2014-03-05 10:35:14 UTC (rev 165102)
+++ trunk/Source/WebCore/ChangeLog        2014-03-05 10:41:48 UTC (rev 165103)
</span><span class="lines">@@ -1,3 +1,78 @@
</span><ins>+2014-03-05  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        appendChild shouldn't invalidate LiveNodeLists and HTMLCollections if they don't have valid caches
+        https://bugs.webkit.org/show_bug.cgi?id=129727
+
+        Reviewed by Andreas Kling.
+
+        Before this patch, invalidateNodeListAndCollectionCachesInAncestors invalidated node lists and HTML
+        collections on ancestors of a node whenever we're inserting or removing a child node. This patch
+        makes HTMLCollections and LiveNodeLists register themselves with Document only when they have valid
+        caches.
+
+        Each user of CollectionIndexCache now implements willValidateIndexCache member function that gets
+        called when CollectionIndexCache caches any state and necessitates the registration with document.
+
+        * dom/ChildNodeList.h: Added an empty willValidateIndexCache since child node lists are never
+        registered with document.
+
+        * dom/CollectionIndexCache.h:
+        (WebCore::CollectionIndexCache::hasValidCache): Added.
+        (WebCore::CollectionIndexCache::nodeCount): Calls willValidateIndexCache when caching node count.
+        (WebCore::CollectionIndexCache::nodeAfterCached): Ditto. Also assert that hasValidCache() true in
+        the cases where we're simply updating our caches or adding more caches.
+        (WebCore::CollectionIndexCache::nodeAt): Ditto. Also added a code to set the length cache when
+        we've reached the end of the list. This should be a slight speed up on some cases.
+
+        * dom/Document.cpp:
+        (WebCore::Document::Document): Initializes a variable used by assertions.
+        (WebCore::Document::unregisterNodeList): Added an early exit for when m_listsInvalidatedAtDocument
+        is empty since invalidateNodeListAndCollectionCaches swaps out the list.
+        (WebCore::Document::registerCollection): Removed the boolean hasIdNameMap since we now explicitly
+        call collectionCachedIdNameMap in HTMLCollection.
+        (WebCore::Document::unregisterCollection): Ditto. Exit early if m_collectionsInvalidatedAtDocument
+        is empty since invalidateNodeListAndCollectionCaches swaps out the list.
+        * dom/Document.h:
+
+        * dom/LiveNodeList.cpp:
+        (WebCore::LiveNodeList::invalidateCache): Unregister the node list with document if we had caches.
+        * dom/LiveNodeList.h:
+        (WebCore::LiveNodeList::LiveNodeList):
+        (WebCore::LiveNodeList::~LiveNodeList): Ditto.
+        (WebCore::LiveNodeList::invalidateCache): Pass around document. This is necessary since document()
+        had already moved to the new document inside NodeListsNodeData::invalidateCaches.
+        (WebCore::LiveNodeList::willValidateIndexCache): Added. Registers itself with document.
+
+        * dom/Node.cpp:
+        (WebCore::Document::invalidateNodeListAndCollectionCaches): Swap the lists since invalidateCache
+        tries to unregister node lists and HTML collections with document. Since this is the only case in
+        which node lists and HTML collections being removed may not be in the lists in unregisterNodeList
+        and unregisterCollection, assert this condition via m_inInvalidateNodeListAndCollectionCaches.
+        (WebCore::NodeListsNodeData::invalidateCaches):
+
+        * dom/NodeRareData.h:
+        (WebCore::NodeListsNodeData::adoptDocument): Unregister node lists and HTML collections from old
+        document via invalidateCache. We need to explicitly pass in oldDocument here since owner node's
+        document had already been changed to newDocument at this point. Since we're invalidating caches,
+        there is no need to register node lists and HTML collections with newDocument.
+
+        * html/HTMLCollection.cpp:
+        (WebCore::HTMLCollection::HTMLCollection):
+        (WebCore::HTMLCollection::~HTMLCollection): Unregister the node list with document if we had caches.
+        (WebCore::HTMLCollection::invalidateCache): Ditto.
+        (WebCore::HTMLCollection::invalidateNamedElementCache):
+        * html/HTMLCollection.h:
+        (WebCore::HTMLCollection::invalidateCache): Pass around document as done in LiveNodeList.
+        (WebCore::HTMLCollection::willValidateIndexCache): Ditto.
+
+        * html/HTMLFormControlsCollection.cpp:
+        (WebCore::HTMLFormControlsCollection::invalidateCache): Ditto.
+        * html/HTMLFormControlsCollection.h:
+
+        * html/HTMLSelectElement.cpp:
+        (WebCore::HTMLSelectElement::invalidateSelectedItems): Ditto.
+        (WebCore::HTMLSelectElement::setRecalcListItems): Ditto.
+
</ins><span class="cx"> 2014-03-05  Jon Lee  &lt;jonlee@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Fix linker error after r165087
</span></span></pre></div>
<a id="trunkSourceWebCoredomChildNodeListh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/ChildNodeList.h (165102 => 165103)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/ChildNodeList.h        2014-03-05 10:35:14 UTC (rev 165102)
+++ trunk/Source/WebCore/dom/ChildNodeList.h        2014-03-05 10:41:48 UTC (rev 165103)
</span><span class="lines">@@ -75,6 +75,7 @@
</span><span class="cx">     Node* collectionTraverseForward(Node&amp;, unsigned count, unsigned&amp; traversedCount) const;
</span><span class="cx">     Node* collectionTraverseBackward(Node&amp;, unsigned count) const;
</span><span class="cx">     bool collectionCanTraverseBackward() const { return true; }
</span><ins>+    void willValidateIndexCache() const { }
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     explicit ChildNodeList(ContainerNode&amp; parent);
</span></span></pre></div>
<a id="trunkSourceWebCoredomCollectionIndexCacheh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/CollectionIndexCache.h (165102 => 165103)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/CollectionIndexCache.h        2014-03-05 10:35:14 UTC (rev 165102)
+++ trunk/Source/WebCore/dom/CollectionIndexCache.h        2014-03-05 10:41:48 UTC (rev 165103)
</span><span class="lines">@@ -40,10 +40,12 @@
</span><span class="cx">     unsigned nodeCount(const Collection&amp;);
</span><span class="cx">     NodeType* nodeAt(const Collection&amp;, unsigned index);
</span><span class="cx"> 
</span><ins>+    bool hasValidCache() const { return m_currentNode || m_nodeCountValid || m_listValid; }
</ins><span class="cx">     void invalidate();
</span><span class="cx">     size_t memoryCost() { return m_cachedList.capacity() * sizeof(NodeType*); }
</span><span class="cx"> 
</span><span class="cx"> private:
</span><ins>+
</ins><span class="cx">     unsigned computeNodeCountUpdatingListCache(const Collection&amp;);
</span><span class="cx">     NodeType* nodeBeforeCached(const Collection&amp;, unsigned);
</span><span class="cx">     NodeType* nodeAfterCached(const Collection&amp;, unsigned);
</span><span class="lines">@@ -70,6 +72,8 @@
</span><span class="cx"> inline unsigned CollectionIndexCache&lt;Collection, NodeType&gt;::nodeCount(const Collection&amp; collection)
</span><span class="cx"> {
</span><span class="cx">     if (!m_nodeCountValid) {
</span><ins>+        if (!hasValidCache())
+            collection.willValidateIndexCache();
</ins><span class="cx">         m_nodeCount = computeNodeCountUpdatingListCache(collection);
</span><span class="cx">         m_nodeCountValid = true;
</span><span class="cx">     }
</span><span class="lines">@@ -132,6 +136,7 @@
</span><span class="cx"> 
</span><span class="cx">     bool lastIsCloser = m_nodeCountValid &amp;&amp; m_nodeCount - index &lt; index - m_currentIndex;
</span><span class="cx">     if (lastIsCloser &amp;&amp; collection.collectionCanTraverseBackward()) {
</span><ins>+        ASSERT(hasValidCache());
</ins><span class="cx">         m_currentNode = collection.collectionLast();
</span><span class="cx">         if (index &lt; m_nodeCount - 1)
</span><span class="cx">             m_currentNode = collection.collectionTraverseBackward(*m_currentNode, m_nodeCount - index - 1);
</span><span class="lines">@@ -140,6 +145,9 @@
</span><span class="cx">         return m_currentNode;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    if (!hasValidCache())
+        collection.willValidateIndexCache();
+
</ins><span class="cx">     unsigned traversedCount;
</span><span class="cx">     m_currentNode = collection.collectionTraverseForward(*m_currentNode, index - m_currentIndex, traversedCount);
</span><span class="cx">     m_currentIndex = m_currentIndex + traversedCount;
</span><span class="lines">@@ -151,6 +159,7 @@
</span><span class="cx">         m_nodeCount = m_currentIndex + 1;
</span><span class="cx">         m_nodeCountValid = true;
</span><span class="cx">     }
</span><ins>+    ASSERT(hasValidCache());
</ins><span class="cx">     return m_currentNode;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -173,6 +182,7 @@
</span><span class="cx"> 
</span><span class="cx">     bool lastIsCloser = m_nodeCountValid &amp;&amp; m_nodeCount - index &lt; index;
</span><span class="cx">     if (lastIsCloser &amp;&amp; collection.collectionCanTraverseBackward()) {
</span><ins>+        ASSERT(hasValidCache());
</ins><span class="cx">         m_currentNode = collection.collectionLast();
</span><span class="cx">         if (index &lt; m_nodeCount - 1)
</span><span class="cx">             m_currentNode = collection.collectionTraverseBackward(*m_currentNode, m_nodeCount - index - 1);
</span><span class="lines">@@ -181,12 +191,21 @@
</span><span class="cx">         return m_currentNode;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    if (!hasValidCache())
+        collection.willValidateIndexCache();
+
</ins><span class="cx">     m_currentNode = collection.collectionFirst();
</span><span class="cx">     m_currentIndex = 0;
</span><span class="cx">     if (index &amp;&amp; m_currentNode) {
</span><span class="cx">         m_currentNode = collection.collectionTraverseForward(*m_currentNode, index, m_currentIndex);
</span><span class="cx">         ASSERT(m_currentNode || m_currentIndex &lt; index);
</span><span class="cx">     }
</span><ins>+    if (!m_currentNode &amp;&amp; !m_nodeCountValid) {
+        // Failed to find the index but at least we now know the size.
+        m_nodeCount = m_currentIndex + 1;
+        m_nodeCountValid = true;
+    }
+    ASSERT(hasValidCache());
</ins><span class="cx">     return m_currentNode;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumentcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.cpp (165102 => 165103)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.cpp        2014-03-05 10:35:14 UTC (rev 165102)
+++ trunk/Source/WebCore/dom/Document.cpp        2014-03-05 10:41:48 UTC (rev 165103)
</span><span class="lines">@@ -454,6 +454,9 @@
</span><span class="cx">     , m_xmlStandalone(StandaloneUnspecified)
</span><span class="cx">     , m_hasXMLDeclaration(false)
</span><span class="cx">     , m_designMode(inherit)
</span><ins>+#if !ASSERT_DISABLED
+    , m_inInvalidateNodeListAndCollectionCaches(false)
+#endif
</ins><span class="cx"> #if ENABLE(DASHBOARD_SUPPORT)
</span><span class="cx">     , m_hasAnnotatedRegions(false)
</span><span class="cx">     , m_annotatedRegionsDirty(false)
</span><span class="lines">@@ -3459,27 +3462,31 @@
</span><span class="cx"> {
</span><span class="cx">     m_nodeListAndCollectionCounts[list.invalidationType()]--;
</span><span class="cx">     if (list.isRootedAtDocument()) {
</span><ins>+        if (!m_listsInvalidatedAtDocument.size()) {
+            ASSERT(m_inInvalidateNodeListAndCollectionCaches);
+            return;
+        }
</ins><span class="cx">         ASSERT(m_listsInvalidatedAtDocument.contains(&amp;list));
</span><span class="cx">         m_listsInvalidatedAtDocument.remove(&amp;list);
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void Document::registerCollection(HTMLCollection&amp; collection, bool hasIdNameMap)
</del><ins>+void Document::registerCollection(HTMLCollection&amp; collection)
</ins><span class="cx"> {
</span><del>-    if (hasIdNameMap)
-        collectionCachedIdNameMap(collection);
</del><span class="cx">     m_nodeListAndCollectionCounts[collection.invalidationType()]++;
</span><span class="cx">     if (collection.isRootedAtDocument())
</span><span class="cx">         m_collectionsInvalidatedAtDocument.add(&amp;collection);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void Document::unregisterCollection(HTMLCollection&amp; collection, bool hasIdNameMap)
</del><ins>+void Document::unregisterCollection(HTMLCollection&amp; collection)
</ins><span class="cx"> {
</span><del>-    if (hasIdNameMap)
-        collectionWillClearIdNameMap(collection);
</del><span class="cx">     ASSERT(m_nodeListAndCollectionCounts[collection.invalidationType()]);
</span><span class="cx">     m_nodeListAndCollectionCounts[collection.invalidationType()]--;
</span><span class="cx">     if (collection.isRootedAtDocument()) {
</span><ins>+        if (!m_collectionsInvalidatedAtDocument.size()) {
+            ASSERT(m_inInvalidateNodeListAndCollectionCaches);
+            return;
+        }
</ins><span class="cx">         ASSERT(m_collectionsInvalidatedAtDocument.contains(&amp;collection));
</span><span class="cx">         m_collectionsInvalidatedAtDocument.remove(&amp;collection);
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumenth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.h (165102 => 165103)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.h        2014-03-05 10:35:14 UTC (rev 165102)
+++ trunk/Source/WebCore/dom/Document.h        2014-03-05 10:41:48 UTC (rev 165103)
</span><span class="lines">@@ -766,8 +766,8 @@
</span><span class="cx"> 
</span><span class="cx">     void registerNodeList(LiveNodeList&amp;);
</span><span class="cx">     void unregisterNodeList(LiveNodeList&amp;);
</span><del>-    void registerCollection(HTMLCollection&amp;, bool hasNamedElementCache);
-    void unregisterCollection(HTMLCollection&amp;, bool hasNamedElementCache);
</del><ins>+    void registerCollection(HTMLCollection&amp;);
+    void unregisterCollection(HTMLCollection&amp;);
</ins><span class="cx">     void collectionCachedIdNameMap(const HTMLCollection&amp;);
</span><span class="cx">     void collectionWillClearIdNameMap(const HTMLCollection&amp;);
</span><span class="cx">     bool shouldInvalidateNodeListAndCollectionCaches(const QualifiedName* attrName = nullptr) const;
</span><span class="lines">@@ -1506,6 +1506,9 @@
</span><span class="cx"> 
</span><span class="cx">     HashSet&lt;LiveNodeList*&gt; m_listsInvalidatedAtDocument;
</span><span class="cx">     HashSet&lt;HTMLCollection*&gt; m_collectionsInvalidatedAtDocument;
</span><ins>+#if !ASSERT_DISABLED
+    bool m_inInvalidateNodeListAndCollectionCaches;
+#endif
</ins><span class="cx"> 
</span><span class="cx">     unsigned m_nodeListAndCollectionCounts[numNodeListInvalidationTypes];
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoredomLiveNodeListcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/LiveNodeList.cpp (165102 => 165103)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/LiveNodeList.cpp        2014-03-05 10:35:14 UTC (rev 165102)
+++ trunk/Source/WebCore/dom/LiveNodeList.cpp        2014-03-05 10:41:48 UTC (rev 165103)
</span><span class="lines">@@ -148,8 +148,11 @@
</span><span class="cx">     return m_indexCache.memoryCost();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void LiveNodeList::invalidateCache() const
</del><ins>+void LiveNodeList::invalidateCache(Document&amp; document) const
</ins><span class="cx"> {
</span><ins>+    if (!m_indexCache.hasValidCache())
+        return;
+    document.unregisterNodeList(const_cast&lt;LiveNodeList&amp;&gt;(*this));
</ins><span class="cx">     m_indexCache.invalidate();
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoredomLiveNodeListh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/LiveNodeList.h (165102 => 165103)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/LiveNodeList.h        2014-03-05 10:35:14 UTC (rev 165102)
+++ trunk/Source/WebCore/dom/LiveNodeList.h        2014-03-05 10:41:48 UTC (rev 165103)
</span><span class="lines">@@ -63,15 +63,14 @@
</span><span class="cx">         ASSERT(m_rootType == static_cast&lt;unsigned&gt;(rootType));
</span><span class="cx">         ASSERT(m_invalidationType == static_cast&lt;unsigned&gt;(invalidationType));
</span><span class="cx">         ASSERT(m_type == static_cast&lt;unsigned&gt;(type));
</span><del>-
-        document().registerNodeList(*this);
</del><span class="cx">     }
</span><span class="cx">     virtual Node* namedItem(const AtomicString&amp;) const override final;
</span><span class="cx">     virtual bool nodeMatches(Element*) const = 0;
</span><span class="cx"> 
</span><span class="cx">     virtual ~LiveNodeList()
</span><span class="cx">     {
</span><del>-        document().unregisterNodeList(*this);
</del><ins>+        if (m_indexCache.hasValidCache())
+            document().unregisterNodeList(*this);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // DOM API
</span><span class="lines">@@ -86,9 +85,9 @@
</span><span class="cx">     ALWAYS_INLINE void invalidateCache(const QualifiedName* attrName) const
</span><span class="cx">     {
</span><span class="cx">         if (!attrName || shouldInvalidateTypeOnAttributeChange(invalidationType(), *attrName))
</span><del>-            invalidateCache();
</del><ins>+            invalidateCache(document());
</ins><span class="cx">     }
</span><del>-    void invalidateCache() const;
</del><ins>+    void invalidateCache(Document&amp;) const;
</ins><span class="cx"> 
</span><span class="cx">     // For CollectionIndexCache
</span><span class="cx">     Element* collectionFirst() const;
</span><span class="lines">@@ -96,6 +95,7 @@
</span><span class="cx">     Element* collectionTraverseForward(Element&amp;, unsigned count, unsigned&amp; traversedCount) const;
</span><span class="cx">     Element* collectionTraverseBackward(Element&amp;, unsigned count) const;
</span><span class="cx">     bool collectionCanTraverseBackward() const { return true; }
</span><ins>+    void willValidateIndexCache() const { document().registerNodeList(const_cast&lt;LiveNodeList&amp;&gt;(*this)); }
</ins><span class="cx"> 
</span><span class="cx"> protected:
</span><span class="cx">     Document&amp; document() const { return m_ownerNode-&gt;document(); }
</span></span></pre></div>
<a id="trunkSourceWebCoredomNodecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Node.cpp (165102 => 165103)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Node.cpp        2014-03-05 10:35:14 UTC (rev 165102)
+++ trunk/Source/WebCore/dom/Node.cpp        2014-03-05 10:41:48 UTC (rev 165103)
</span><span class="lines">@@ -721,10 +721,19 @@
</span><span class="cx"> 
</span><span class="cx"> void Document::invalidateNodeListAndCollectionCaches(const QualifiedName* attrName)
</span><span class="cx"> {
</span><del>-    for (HashSet&lt;LiveNodeList*&gt;::iterator it = m_listsInvalidatedAtDocument.begin(), end = m_listsInvalidatedAtDocument.end(); it != end; ++it)
-        (*it)-&gt;invalidateCache(attrName);
-    for (HashSet&lt;HTMLCollection*&gt;::iterator it = m_collectionsInvalidatedAtDocument.begin(), end = m_collectionsInvalidatedAtDocument.end(); it != end; ++it)
-        (*it)-&gt;invalidateCache(attrName);
</del><ins>+#if !ASSERT_DISABLED
+    m_inInvalidateNodeListAndCollectionCaches = true;
+#endif
+    HashSet&lt;LiveNodeList*&gt; liveNodeLists = std::move(m_listsInvalidatedAtDocument);
+    for (auto it : liveNodeLists)
+        it-&gt;invalidateCache(attrName);
+
+    HashSet&lt;HTMLCollection*&gt; collectionLists = std::move(m_collectionsInvalidatedAtDocument);
+    for (auto it : collectionLists)
+        it-&gt;invalidateCache(attrName);
+#if !ASSERT_DISABLED
+    m_inInvalidateNodeListAndCollectionCaches = false;
+#endif
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void Node::invalidateNodeListAndCollectionCachesInAncestors(const QualifiedName* attrName, Element* attributeOwnerElement)
</span><span class="lines">@@ -1689,7 +1698,7 @@
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     for (auto&amp; tagNodeList : m_tagNodeListCacheNS)
</span><del>-        tagNodeList.value-&gt;invalidateCache();
</del><ins>+        tagNodeList.value-&gt;invalidateCache(nullptr);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void Node::getSubresourceURLs(ListHashSet&lt;URL&gt;&amp; urls) const
</span></span></pre></div>
<a id="trunkSourceWebCoredomNodeRareDatah"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/NodeRareData.h (165102 => 165103)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/NodeRareData.h        2014-03-05 10:35:14 UTC (rev 165102)
+++ trunk/Source/WebCore/dom/NodeRareData.h        2014-03-05 10:41:48 UTC (rev 165103)
</span><span class="lines">@@ -225,39 +225,26 @@
</span><span class="cx"> 
</span><span class="cx">     void adoptDocument(Document* oldDocument, Document* newDocument)
</span><span class="cx">     {
</span><ins>+        ASSERT(oldDocument);
</ins><span class="cx">         if (oldDocument == newDocument) {
</span><span class="cx">             invalidateCaches();
</span><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        for (auto it : m_atomicNameCaches) {
-            LiveNodeList&amp; list = *it.value;
-            oldDocument-&gt;unregisterNodeList(list);
-            newDocument-&gt;registerNodeList(list);
-            list.invalidateCache();
-        }
</del><ins>+        for (auto it : m_atomicNameCaches)
+            it.value-&gt;invalidateCache(*oldDocument);
</ins><span class="cx"> 
</span><del>-        for (auto it : m_nameCaches) {
-            LiveNodeList&amp; list = *it.value;
-            oldDocument-&gt;unregisterNodeList(list);
-            newDocument-&gt;registerNodeList(list);
-            list.invalidateCache();
-        }
</del><ins>+        for (auto it : m_nameCaches)
+            it.value-&gt;invalidateCache(*oldDocument);
</ins><span class="cx"> 
</span><span class="cx">         for (auto it : m_tagNodeListCacheNS) {
</span><span class="cx">             LiveNodeList&amp; list = *it.value;
</span><span class="cx">             ASSERT(!list.isRootedAtDocument());
</span><del>-            oldDocument-&gt;unregisterNodeList(list);
-            newDocument-&gt;registerNodeList(list);
-            list.invalidateCache();
</del><ins>+            list.invalidateCache(*oldDocument);
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        for (auto it : m_cachedCollections) {
-            HTMLCollection&amp; collection = *it.value;
-            oldDocument-&gt;unregisterCollection(collection, collection.hasNamedElementCache());
-            newDocument-&gt;registerCollection(collection, collection.hasNamedElementCache());
-            collection.invalidateCache();
-        }
</del><ins>+        for (auto it : m_cachedCollections)
+            it.value-&gt;invalidateCache(*oldDocument);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx"> private:
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLCollectioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLCollection.cpp (165102 => 165103)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLCollection.cpp        2014-03-05 10:35:14 UTC (rev 165102)
+++ trunk/Source/WebCore/html/HTMLCollection.cpp        2014-03-05 10:41:48 UTC (rev 165103)
</span><span class="lines">@@ -142,8 +142,6 @@
</span><span class="cx">     ASSERT(m_rootType == static_cast&lt;unsigned&gt;(rootTypeFromCollectionType(type)));
</span><span class="cx">     ASSERT(m_invalidationType == static_cast&lt;unsigned&gt;(invalidationTypeExcludingIdAndNameAttributes(type)));
</span><span class="cx">     ASSERT(m_collectionType == static_cast&lt;unsigned&gt;(type));
</span><del>-
-    document().registerCollection(*this, hasNamedElementCache());
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> PassRefPtr&lt;HTMLCollection&gt; HTMLCollection::create(ContainerNode&amp; base, CollectionType type)
</span><span class="lines">@@ -153,7 +151,10 @@
</span><span class="cx"> 
</span><span class="cx"> HTMLCollection::~HTMLCollection()
</span><span class="cx"> {
</span><del>-    document().unregisterCollection(*this, hasNamedElementCache());
</del><ins>+    if (m_indexCache.hasValidCache())
+        document().unregisterCollection(*this);
+    if (hasNamedElementCache())
+        document().collectionWillClearIdNameMap(*this);
</ins><span class="cx">     // HTMLNameCollection removes cache by itself.
</span><span class="cx">     if (type() != WindowNamedItems &amp;&amp; type() != DocumentNamedItems)
</span><span class="cx">         ownerNode().nodeLists()-&gt;removeCachedCollection(this);
</span><span class="lines">@@ -362,17 +363,20 @@
</span><span class="cx">     return element;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void HTMLCollection::invalidateCache() const
</del><ins>+void HTMLCollection::invalidateCache(Document&amp; document) const
</ins><span class="cx"> {
</span><del>-    m_indexCache.invalidate();
</del><ins>+    if (m_indexCache.hasValidCache()) {
+        document.unregisterCollection(const_cast&lt;HTMLCollection&amp;&gt;(*this));
+        m_indexCache.invalidate();
+    }
</ins><span class="cx">     if (hasNamedElementCache())
</span><del>-        invalidateNamedElementCache();
</del><ins>+        invalidateNamedElementCache(document);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void HTMLCollection::invalidateNamedElementCache() const
</del><ins>+void HTMLCollection::invalidateNamedElementCache(Document&amp; document) const
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(hasNamedElementCache());
</span><del>-    document().collectionWillClearIdNameMap(*this);
</del><ins>+    document.collectionWillClearIdNameMap(*this);
</ins><span class="cx">     m_namedElementCache = nullptr;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLCollectionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLCollection.h (165102 => 165103)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLCollection.h        2014-03-05 10:35:14 UTC (rev 165102)
+++ trunk/Source/WebCore/html/HTMLCollection.h        2014-03-05 10:41:48 UTC (rev 165103)
</span><span class="lines">@@ -107,11 +107,11 @@
</span><span class="cx">     void invalidateCache(const QualifiedName* attrName) const
</span><span class="cx">     {
</span><span class="cx">         if (!attrName || shouldInvalidateTypeOnAttributeChange(invalidationType(), *attrName))
</span><del>-            invalidateCache();
</del><ins>+            invalidateCache(document());
</ins><span class="cx">         else if (hasNamedElementCache() &amp;&amp; (*attrName == HTMLNames::idAttr || *attrName == HTMLNames::nameAttr))
</span><del>-            invalidateNamedElementCache();
</del><ins>+            invalidateNamedElementCache(document());
</ins><span class="cx">     }
</span><del>-    virtual void invalidateCache() const;
</del><ins>+    virtual void invalidateCache(Document&amp;) const;
</ins><span class="cx"> 
</span><span class="cx">     // For CollectionIndexCache
</span><span class="cx">     Element* collectionFirst() const;
</span><span class="lines">@@ -119,6 +119,7 @@
</span><span class="cx">     Element* collectionTraverseForward(Element&amp;, unsigned count, unsigned&amp; traversedCount) const;
</span><span class="cx">     Element* collectionTraverseBackward(Element&amp;, unsigned count) const;
</span><span class="cx">     bool collectionCanTraverseBackward() const { return !m_usesCustomForwardOnlyTraversal; }
</span><ins>+    void willValidateIndexCache() const { document().registerCollection(const_cast&lt;HTMLCollection&amp;&gt;(*this)); }
</ins><span class="cx"> 
</span><span class="cx">     bool hasNamedElementCache() const { return !!m_namedElementCache; }
</span><span class="cx"> 
</span><span class="lines">@@ -155,7 +156,7 @@
</span><span class="cx"> 
</span><span class="cx">     virtual Element* customElementAfter(Element*) const { ASSERT_NOT_REACHED(); return nullptr; }
</span><span class="cx">     
</span><del>-    void invalidateNamedElementCache() const;
</del><ins>+    void invalidateNamedElementCache(Document&amp;) const;
</ins><span class="cx"> 
</span><span class="cx">     Ref&lt;ContainerNode&gt; m_ownerNode;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLFormControlsCollectioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLFormControlsCollection.cpp (165102 => 165103)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLFormControlsCollection.cpp        2014-03-05 10:35:14 UTC (rev 165102)
+++ trunk/Source/WebCore/html/HTMLFormControlsCollection.cpp        2014-03-05 10:41:48 UTC (rev 165103)
</span><span class="lines">@@ -177,9 +177,9 @@
</span><span class="cx">     cache.didPopulate();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void HTMLFormControlsCollection::invalidateCache() const
</del><ins>+void HTMLFormControlsCollection::invalidateCache(Document&amp; document) const
</ins><span class="cx"> {
</span><del>-    HTMLCollection::invalidateCache();
</del><ins>+    HTMLCollection::invalidateCache(document);
</ins><span class="cx">     m_cachedElement = nullptr;
</span><span class="cx">     m_cachedElementOffsetInArray = 0;
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLFormControlsCollectionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLFormControlsCollection.h (165102 => 165103)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLFormControlsCollection.h        2014-03-05 10:35:14 UTC (rev 165102)
+++ trunk/Source/WebCore/html/HTMLFormControlsCollection.h        2014-03-05 10:41:48 UTC (rev 165103)
</span><span class="lines">@@ -46,7 +46,7 @@
</span><span class="cx"> private:
</span><span class="cx">     explicit HTMLFormControlsCollection(ContainerNode&amp;);
</span><span class="cx"> 
</span><del>-    virtual void invalidateCache() const override;
</del><ins>+    virtual void invalidateCache(Document&amp;) const override;
</ins><span class="cx">     virtual void updateNamedElementCache() const override;
</span><span class="cx"> 
</span><span class="cx">     const Vector&lt;FormAssociatedElement*&gt;&amp; formControlElements() const;
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLSelectElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLSelectElement.cpp (165102 => 165103)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLSelectElement.cpp        2014-03-05 10:35:14 UTC (rev 165102)
+++ trunk/Source/WebCore/html/HTMLSelectElement.cpp        2014-03-05 10:41:48 UTC (rev 165103)
</span><span class="lines">@@ -750,7 +750,7 @@
</span><span class="cx"> void HTMLSelectElement::invalidateSelectedItems()
</span><span class="cx"> {
</span><span class="cx">     if (HTMLCollection* collection = cachedHTMLCollection(SelectedOptions))
</span><del>-        collection-&gt;invalidateCache();
</del><ins>+        collection-&gt;invalidateCache(document());
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void HTMLSelectElement::setRecalcListItems()
</span><span class="lines">@@ -762,7 +762,7 @@
</span><span class="cx">     setNeedsStyleRecalc();
</span><span class="cx">     if (!inDocument()) {
</span><span class="cx">         if (HTMLCollection* collection = cachedHTMLCollection(SelectOptions))
</span><del>-            collection-&gt;invalidateCache();
</del><ins>+            collection-&gt;invalidateCache(document());
</ins><span class="cx">     }
</span><span class="cx">     if (!inDocument())
</span><span class="cx">         invalidateSelectedItems();
</span></span></pre>
</div>
</div>

</body>
</html>