HTMLCollection should not be NodeList
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Nov 2013 13:20:49 +0000 (13:20 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Nov 2013 13:20:49 +0000 (13:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=123794

Reviewed by Andreas Kling.

HTMLCollection and NodeList are unrelated types in DOM yet our HTMLCollection inherits NodeList
for code sharing reasons. While some code does get shared the types are sufficiently different
that this results in lots of unnecessary branches, complexity and general awkwardness. Code sharing
can be better achieved by means other than inheritance.

This patch splits HTMLCollection from NodeList by copy-pasting and eliminating resulting redundancies.
Sharing comes later.

* dom/Attr.cpp:
(WebCore::Attr::setValue):
(WebCore::Attr::childrenChanged):
* dom/ClassNodeList.cpp:
(WebCore::ClassNodeList::~ClassNodeList):
* dom/ContainerNode.cpp:
(WebCore::ContainerNode::childrenChanged):
(WebCore::ContainerNode::getElementsByTagName):
(WebCore::ContainerNode::getElementsByName):
(WebCore::ContainerNode::getElementsByClassName):
(WebCore::ContainerNode::radioNodeList):
* dom/Document.cpp:
(WebCore::Document::Document):
(WebCore::Document::~Document):
(WebCore::Document::registerNodeList):
(WebCore::Document::unregisterNodeList):
(WebCore::Document::registerCollection):
(WebCore::Document::unregisterCollection):
(WebCore::Document::ensureCachedCollection):

    Add separate functions and map for registering HTMLCollections.

(WebCore::Document::all):
(WebCore::Document::windowNamedItems):
(WebCore::Document::documentNamedItems):
* dom/Document.h:
* dom/Element.cpp:
(WebCore::Element::attributeChanged):
(WebCore::Element::ensureCachedHTMLCollection):
(WebCore::Element::cachedHTMLCollection):
* dom/LiveNodeList.cpp:
(WebCore::LiveNodeList::rootNode):
(WebCore::isMatchingElement):
(WebCore::LiveNodeList::iterateForPreviousElement):
(WebCore::LiveNodeList::itemBefore):
(WebCore::firstMatchingElement):
(WebCore::nextMatchingElement):
(WebCore::traverseMatchingElementsForwardToOffset):
(WebCore::LiveNodeList::traverseLiveNodeListFirstElement):
(WebCore::LiveNodeList::traverseLiveNodeListForwardToOffset):
(WebCore::LiveNodeList::isLastItemCloserThanLastOrCachedItem):
(WebCore::LiveNodeList::isFirstItemCloserThanCachedItem):
(WebCore::LiveNodeList::length):
(WebCore::LiveNodeList::item):
(WebCore::LiveNodeList::elementBeforeOrAfterCachedElement):

    This code used to live in HTMLCollection.cpp. Copy-paste here and remove all branches not needed for NodeLists.

(WebCore::LiveNodeList::invalidateCache):

    NodeLists have no name caches.

* dom/LiveNodeList.h:
(WebCore::LiveNodeList::LiveNodeList):
(WebCore::LiveNodeList::~LiveNodeList):
(WebCore::LiveNodeList::isRootedAtDocument):
(WebCore::LiveNodeList::type):
(WebCore::LiveNodeList::invalidateCache):
(WebCore::LiveNodeList::setCachedElement):

    Merge LiveNodeListBase and LiveNodeList.
    Remove fields and code supporting HTMLCollection.

(WebCore::shouldInvalidateTypeOnAttributeChange):

    Move to global scope. This function is used both HTMLCollections and LiveNodeLists.

* dom/NameNodeList.cpp:
(WebCore::NameNodeList::~NameNodeList):
* dom/NameNodeList.h:
(WebCore::NameNodeList::create):
* dom/Node.cpp:
(WebCore::shouldInvalidateNodeListCachesForAttr):
(WebCore::Document::shouldInvalidateNodeListAndCollectionCaches):
(WebCore::Document::invalidateNodeListAndCollectionCaches):
(WebCore::Node::invalidateNodeListAndCollectionCachesInAncestors):
(WebCore::NodeListsNodeData::invalidateCaches):
* dom/Node.h:
* dom/NodeRareData.h:
(WebCore::NodeListsNodeData::addCacheWithAtomicName):
(WebCore::NodeListsNodeData::addCacheWithName):
(WebCore::NodeListsNodeData::addCacheWithQualifiedName):
(WebCore::NodeListsNodeData::addCachedCollection):
(WebCore::NodeListsNodeData::cachedCollection):
(WebCore::NodeListsNodeData::removeCacheWithAtomicName):
(WebCore::NodeListsNodeData::removeCacheWithName):
(WebCore::NodeListsNodeData::removeCachedCollection):
(WebCore::NodeListsNodeData::isEmpty):
(WebCore::NodeListsNodeData::adoptDocument):
(WebCore::NodeListsNodeData::namedCollectionKey):
(WebCore::NodeListsNodeData::namedNodeListKey):
(WebCore::NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList):

    Add separate cache for HTMLCollections.

* dom/TagNodeList.cpp:
(WebCore::TagNodeList::TagNodeList):
(WebCore::TagNodeList::~TagNodeList):
* dom/TagNodeList.h:
(WebCore::TagNodeList::create):
(WebCore::HTMLTagNodeList::create):
* html/CollectionType.h:

    Remove NodeList types.

* html/HTMLCollection.cpp:
(WebCore::shouldOnlyIncludeDirectChildren):
(WebCore::rootTypeFromCollectionType):
(WebCore::invalidationTypeExcludingIdAndNameAttributes):
(WebCore::HTMLCollection::HTMLCollection):
(WebCore::HTMLCollection::~HTMLCollection):
(WebCore::HTMLCollection::rootNode):
(WebCore::isMatchingElement):
(WebCore::HTMLCollection::iterateForPreviousElement):
(WebCore::HTMLCollection::itemBefore):
(WebCore::firstMatchingElement):
(WebCore::nextMatchingElement):
(WebCore::traverseMatchingElementsForwardToOffset):
(WebCore::HTMLCollection::isLastItemCloserThanLastOrCachedItem):
(WebCore::HTMLCollection::isFirstItemCloserThanCachedItem):
(WebCore::HTMLCollection::setCachedElement):
(WebCore::HTMLCollection::length):
(WebCore::HTMLCollection::item):
(WebCore::HTMLCollection::elementBeforeOrAfterCachedElement):
(WebCore::HTMLCollection::traverseFirstElement):
(WebCore::HTMLCollection::traverseNextElement):
(WebCore::HTMLCollection::traverseForwardToOffset):
(WebCore::HTMLCollection::invalidateCache):
(WebCore::HTMLCollection::invalidateIdNameCacheMaps):
(WebCore::HTMLCollection::namedItem):

    Remove NodeList specific branches and functions.
    LiveNodeListBase functions are now HTMLCollection functions.

* html/HTMLCollection.h:
(WebCore::HTMLCollection::isRootedAtDocument):
(WebCore::HTMLCollection::invalidationType):
(WebCore::HTMLCollection::type):
(WebCore::HTMLCollection::ownerNode):
(WebCore::HTMLCollection::invalidateCache):
(WebCore::HTMLCollection::document):
(WebCore::HTMLCollection::overridesItemAfter):
(WebCore::HTMLCollection::isElementCacheValid):
(WebCore::HTMLCollection::cachedElement):
(WebCore::HTMLCollection::cachedElementOffset):
(WebCore::HTMLCollection::isLengthCacheValid):
(WebCore::HTMLCollection::cachedLength):
(WebCore::HTMLCollection::setLengthCache):
(WebCore::HTMLCollection::setCachedElement):
(WebCore::HTMLCollection::isItemRefElementsCacheValid):
(WebCore::HTMLCollection::setItemRefElementsCacheValid):
(WebCore::HTMLCollection::rootType):
(WebCore::HTMLCollection::hasNameCache):
(WebCore::HTMLCollection::setHasNameCache):

    Copy-paste functions and fields from former LiveNodeListBase.

* html/HTMLNameCollection.cpp:
(WebCore::HTMLNameCollection::~HTMLNameCollection):
* html/LabelableElement.cpp:
(WebCore::LabelableElement::labels):
* html/LabelsNodeList.cpp:
(WebCore::LabelsNodeList::~LabelsNodeList):
* html/LabelsNodeList.h:
* html/RadioNodeList.cpp:
(WebCore::RadioNodeList::~RadioNodeList):
* html/RadioNodeList.h:
(WebCore::RadioNodeList::create):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@158663 268f45cc-cd09-0410-ab3c-d52691b4dbfc

25 files changed:
Source/WebCore/ChangeLog
Source/WebCore/dom/Attr.cpp
Source/WebCore/dom/ClassNodeList.cpp
Source/WebCore/dom/ContainerNode.cpp
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/LiveNodeList.cpp
Source/WebCore/dom/LiveNodeList.h
Source/WebCore/dom/NameNodeList.cpp
Source/WebCore/dom/NameNodeList.h
Source/WebCore/dom/Node.cpp
Source/WebCore/dom/Node.h
Source/WebCore/dom/NodeRareData.h
Source/WebCore/dom/TagNodeList.cpp
Source/WebCore/dom/TagNodeList.h
Source/WebCore/html/CollectionType.h
Source/WebCore/html/HTMLCollection.cpp
Source/WebCore/html/HTMLCollection.h
Source/WebCore/html/HTMLNameCollection.cpp
Source/WebCore/html/LabelableElement.cpp
Source/WebCore/html/LabelsNodeList.cpp
Source/WebCore/html/LabelsNodeList.h
Source/WebCore/html/RadioNodeList.cpp
Source/WebCore/html/RadioNodeList.h

index 55abce4..cd71c3b 100644 (file)
@@ -1,3 +1,187 @@
+2013-11-05  Antti Koivisto  <antti@apple.com>
+
+        HTMLCollection should not be NodeList
+        https://bugs.webkit.org/show_bug.cgi?id=123794
+
+        Reviewed by Andreas Kling.
+
+        HTMLCollection and NodeList are unrelated types in DOM yet our HTMLCollection inherits NodeList
+        for code sharing reasons. While some code does get shared the types are sufficiently different 
+        that this results in lots of unnecessary branches, complexity and general awkwardness. Code sharing 
+        can be better achieved by means other than inheritance.
+        
+        This patch splits HTMLCollection from NodeList by copy-pasting and eliminating resulting redundancies. 
+        Sharing comes later.
+
+        * dom/Attr.cpp:
+        (WebCore::Attr::setValue):
+        (WebCore::Attr::childrenChanged):
+        * dom/ClassNodeList.cpp:
+        (WebCore::ClassNodeList::~ClassNodeList):
+        * dom/ContainerNode.cpp:
+        (WebCore::ContainerNode::childrenChanged):
+        (WebCore::ContainerNode::getElementsByTagName):
+        (WebCore::ContainerNode::getElementsByName):
+        (WebCore::ContainerNode::getElementsByClassName):
+        (WebCore::ContainerNode::radioNodeList):
+        * dom/Document.cpp:
+        (WebCore::Document::Document):
+        (WebCore::Document::~Document):
+        (WebCore::Document::registerNodeList):
+        (WebCore::Document::unregisterNodeList):
+        (WebCore::Document::registerCollection):
+        (WebCore::Document::unregisterCollection):
+        (WebCore::Document::ensureCachedCollection):
+        
+            Add separate functions and map for registering HTMLCollections.
+
+        (WebCore::Document::all):
+        (WebCore::Document::windowNamedItems):
+        (WebCore::Document::documentNamedItems):
+        * dom/Document.h:
+        * dom/Element.cpp:
+        (WebCore::Element::attributeChanged):
+        (WebCore::Element::ensureCachedHTMLCollection):
+        (WebCore::Element::cachedHTMLCollection):
+        * dom/LiveNodeList.cpp:
+        (WebCore::LiveNodeList::rootNode):
+        (WebCore::isMatchingElement):
+        (WebCore::LiveNodeList::iterateForPreviousElement):
+        (WebCore::LiveNodeList::itemBefore):
+        (WebCore::firstMatchingElement):
+        (WebCore::nextMatchingElement):
+        (WebCore::traverseMatchingElementsForwardToOffset):
+        (WebCore::LiveNodeList::traverseLiveNodeListFirstElement):
+        (WebCore::LiveNodeList::traverseLiveNodeListForwardToOffset):
+        (WebCore::LiveNodeList::isLastItemCloserThanLastOrCachedItem):
+        (WebCore::LiveNodeList::isFirstItemCloserThanCachedItem):
+        (WebCore::LiveNodeList::length):
+        (WebCore::LiveNodeList::item):
+        (WebCore::LiveNodeList::elementBeforeOrAfterCachedElement):
+        
+            This code used to live in HTMLCollection.cpp. Copy-paste here and remove all branches not needed for NodeLists.
+
+        (WebCore::LiveNodeList::invalidateCache):
+        
+            NodeLists have no name caches.
+
+        * dom/LiveNodeList.h:
+        (WebCore::LiveNodeList::LiveNodeList):
+        (WebCore::LiveNodeList::~LiveNodeList):
+        (WebCore::LiveNodeList::isRootedAtDocument):
+        (WebCore::LiveNodeList::type):
+        (WebCore::LiveNodeList::invalidateCache):
+        (WebCore::LiveNodeList::setCachedElement):
+        
+            Merge LiveNodeListBase and LiveNodeList.
+            Remove fields and code supporting HTMLCollection.
+
+        (WebCore::shouldInvalidateTypeOnAttributeChange):
+        
+            Move to global scope. This function is used both HTMLCollections and LiveNodeLists.
+
+        * dom/NameNodeList.cpp:
+        (WebCore::NameNodeList::~NameNodeList):
+        * dom/NameNodeList.h:
+        (WebCore::NameNodeList::create):
+        * dom/Node.cpp:
+        (WebCore::shouldInvalidateNodeListCachesForAttr):
+        (WebCore::Document::shouldInvalidateNodeListAndCollectionCaches):
+        (WebCore::Document::invalidateNodeListAndCollectionCaches):
+        (WebCore::Node::invalidateNodeListAndCollectionCachesInAncestors):
+        (WebCore::NodeListsNodeData::invalidateCaches):
+        * dom/Node.h:
+        * dom/NodeRareData.h:
+        (WebCore::NodeListsNodeData::addCacheWithAtomicName):
+        (WebCore::NodeListsNodeData::addCacheWithName):
+        (WebCore::NodeListsNodeData::addCacheWithQualifiedName):
+        (WebCore::NodeListsNodeData::addCachedCollection):
+        (WebCore::NodeListsNodeData::cachedCollection):
+        (WebCore::NodeListsNodeData::removeCacheWithAtomicName):
+        (WebCore::NodeListsNodeData::removeCacheWithName):
+        (WebCore::NodeListsNodeData::removeCachedCollection):
+        (WebCore::NodeListsNodeData::isEmpty):
+        (WebCore::NodeListsNodeData::adoptDocument):
+        (WebCore::NodeListsNodeData::namedCollectionKey):
+        (WebCore::NodeListsNodeData::namedNodeListKey):
+        (WebCore::NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList):
+        
+            Add separate cache for HTMLCollections.
+
+        * dom/TagNodeList.cpp:
+        (WebCore::TagNodeList::TagNodeList):
+        (WebCore::TagNodeList::~TagNodeList):
+        * dom/TagNodeList.h:
+        (WebCore::TagNodeList::create):
+        (WebCore::HTMLTagNodeList::create):
+        * html/CollectionType.h:
+        
+            Remove NodeList types.
+
+        * html/HTMLCollection.cpp:
+        (WebCore::shouldOnlyIncludeDirectChildren):
+        (WebCore::rootTypeFromCollectionType):
+        (WebCore::invalidationTypeExcludingIdAndNameAttributes):
+        (WebCore::HTMLCollection::HTMLCollection):
+        (WebCore::HTMLCollection::~HTMLCollection):
+        (WebCore::HTMLCollection::rootNode):
+        (WebCore::isMatchingElement):
+        (WebCore::HTMLCollection::iterateForPreviousElement):
+        (WebCore::HTMLCollection::itemBefore):
+        (WebCore::firstMatchingElement):
+        (WebCore::nextMatchingElement):
+        (WebCore::traverseMatchingElementsForwardToOffset):
+        (WebCore::HTMLCollection::isLastItemCloserThanLastOrCachedItem):
+        (WebCore::HTMLCollection::isFirstItemCloserThanCachedItem):
+        (WebCore::HTMLCollection::setCachedElement):
+        (WebCore::HTMLCollection::length):
+        (WebCore::HTMLCollection::item):
+        (WebCore::HTMLCollection::elementBeforeOrAfterCachedElement):
+        (WebCore::HTMLCollection::traverseFirstElement):
+        (WebCore::HTMLCollection::traverseNextElement):
+        (WebCore::HTMLCollection::traverseForwardToOffset):
+        (WebCore::HTMLCollection::invalidateCache):
+        (WebCore::HTMLCollection::invalidateIdNameCacheMaps):
+        (WebCore::HTMLCollection::namedItem):
+        
+            Remove NodeList specific branches and functions.
+            LiveNodeListBase functions are now HTMLCollection functions.
+
+        * html/HTMLCollection.h:
+        (WebCore::HTMLCollection::isRootedAtDocument):
+        (WebCore::HTMLCollection::invalidationType):
+        (WebCore::HTMLCollection::type):
+        (WebCore::HTMLCollection::ownerNode):
+        (WebCore::HTMLCollection::invalidateCache):
+        (WebCore::HTMLCollection::document):
+        (WebCore::HTMLCollection::overridesItemAfter):
+        (WebCore::HTMLCollection::isElementCacheValid):
+        (WebCore::HTMLCollection::cachedElement):
+        (WebCore::HTMLCollection::cachedElementOffset):
+        (WebCore::HTMLCollection::isLengthCacheValid):
+        (WebCore::HTMLCollection::cachedLength):
+        (WebCore::HTMLCollection::setLengthCache):
+        (WebCore::HTMLCollection::setCachedElement):
+        (WebCore::HTMLCollection::isItemRefElementsCacheValid):
+        (WebCore::HTMLCollection::setItemRefElementsCacheValid):
+        (WebCore::HTMLCollection::rootType):
+        (WebCore::HTMLCollection::hasNameCache):
+        (WebCore::HTMLCollection::setHasNameCache):
+        
+            Copy-paste functions and fields from former LiveNodeListBase.
+
+        * html/HTMLNameCollection.cpp:
+        (WebCore::HTMLNameCollection::~HTMLNameCollection):
+        * html/LabelableElement.cpp:
+        (WebCore::LabelableElement::labels):
+        * html/LabelsNodeList.cpp:
+        (WebCore::LabelsNodeList::~LabelsNodeList):
+        * html/LabelsNodeList.h:
+        * html/RadioNodeList.cpp:
+        (WebCore::RadioNodeList::~RadioNodeList):
+        * html/RadioNodeList.h:
+        (WebCore::RadioNodeList::create):
+
 2013-11-05  Emilio Pozuelo Monfort  <pochu27@gmail.com>
 
         [GTK] Add stubs for missing symbols in dom bindings
index d0eb105..5a47b35 100644 (file)
@@ -119,7 +119,7 @@ void Attr::setValue(const AtomicString& value)
     createTextChild();
     m_ignoreChildrenChanged--;
 
-    invalidateNodeListCachesInAncestors(&m_name, m_element);
+    invalidateNodeListAndCollectionCachesInAncestors(&m_name, m_element);
 }
 
 void Attr::setValue(const AtomicString& value, ExceptionCode&)
@@ -162,7 +162,7 @@ void Attr::childrenChanged(const ChildChange&)
     if (m_ignoreChildrenChanged > 0)
         return;
 
-    invalidateNodeListCachesInAncestors(&qualifiedName(), m_element);
+    invalidateNodeListAndCollectionCachesInAncestors(&qualifiedName(), m_element);
 
     StringBuilder valueBuilder;
     TextNodeTraversal::appendContents(this, valueBuilder);
index c8aa164..2cd99b1 100644 (file)
@@ -44,7 +44,7 @@ ClassNodeList::ClassNodeList(ContainerNode& rootNode, const String& classNames)
 
 ClassNodeList::~ClassNodeList()
 {
-    ownerNode().nodeLists()->removeCacheWithName(this, ClassNodeListType, m_originalClassNames);
+    ownerNode().nodeLists()->removeCacheWithName(this, m_originalClassNames);
 } 
 
 bool ClassNodeList::nodeMatches(Element* testNode) const
index eb21bd8..f50e5bd 100644 (file)
@@ -836,7 +836,7 @@ void ContainerNode::childrenChanged(const ChildChange& change)
     document().incDOMTreeVersion();
     if (change.source == ChildChangeSourceAPI && change.type != TextChanged)
         document().updateRangesAfterChildrenChanged(*this);
-    invalidateNodeListCachesInAncestors();
+    invalidateNodeListAndCollectionCachesInAncestors();
 }
 
 inline static void cloneChildNodesAvoidingDeleteButton(ContainerNode* parent, ContainerNode* clonedParent, HTMLElement* deleteButtonContainerElement)
@@ -1127,8 +1127,8 @@ PassRefPtr<NodeList> ContainerNode::getElementsByTagName(const AtomicString& loc
         return 0;
 
     if (document().isHTMLDocument())
-        return ensureRareData().ensureNodeLists().addCacheWithAtomicName<HTMLTagNodeList>(*this, HTMLTagNodeListType, localName);
-    return ensureRareData().ensureNodeLists().addCacheWithAtomicName<TagNodeList>(*this, TagNodeListType, localName);
+        return ensureRareData().ensureNodeLists().addCacheWithAtomicName<HTMLTagNodeList>(*this, LiveNodeList::HTMLTagNodeListType, localName);
+    return ensureRareData().ensureNodeLists().addCacheWithAtomicName<TagNodeList>(*this, LiveNodeList::TagNodeListType, localName);
 }
 
 PassRefPtr<NodeList> ContainerNode::getElementsByTagNameNS(const AtomicString& namespaceURI, const AtomicString& localName)
@@ -1144,18 +1144,18 @@ PassRefPtr<NodeList> ContainerNode::getElementsByTagNameNS(const AtomicString& n
 
 PassRefPtr<NodeList> ContainerNode::getElementsByName(const String& elementName)
 {
-    return ensureRareData().ensureNodeLists().addCacheWithAtomicName<NameNodeList>(*this, NameNodeListType, elementName);
+    return ensureRareData().ensureNodeLists().addCacheWithAtomicName<NameNodeList>(*this, LiveNodeList::NameNodeListType, elementName);
 }
 
 PassRefPtr<NodeList> ContainerNode::getElementsByClassName(const String& classNames)
 {
-    return ensureRareData().ensureNodeLists().addCacheWithName<ClassNodeList>(*this, ClassNodeListType, classNames);
+    return ensureRareData().ensureNodeLists().addCacheWithName<ClassNodeList>(*this, LiveNodeList::ClassNodeListType, classNames);
 }
 
 PassRefPtr<RadioNodeList> ContainerNode::radioNodeList(const AtomicString& name)
 {
     ASSERT(hasTagName(HTMLNames::formTag) || hasTagName(HTMLNames::fieldsetTag));
-    return ensureRareData().ensureNodeLists().addCacheWithAtomicName<RadioNodeList>(*this, RadioNodeListType, name);
+    return ensureRareData().ensureNodeLists().addCacheWithAtomicName<RadioNodeList>(*this, LiveNodeList::RadioNodeListType, name);
 }
 
 } // namespace WebCore
index 070c87b..ed6a1df 100644 (file)
@@ -499,8 +499,8 @@ Document::Document(Frame* frame, const URL& url, unsigned documentClasses)
     initSecurityContext();
     initDNSPrefetch();
 
-    for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_nodeListCounts); i++)
-        m_nodeListCounts[i] = 0;
+    for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_nodeListAndCollectionCounts); ++i)
+        m_nodeListAndCollectionCounts[i] = 0;
 
     InspectorCounters::incrementCounter(InspectorCounters::DocumentCounter);
 }
@@ -587,9 +587,10 @@ Document::~Document()
         clearRareData();
 
     ASSERT(!m_listsInvalidatedAtDocument.size());
+    ASSERT(!m_collectionsInvalidatedAtDocument.size());
 
-    for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_nodeListCounts); i++)
-        ASSERT(!m_nodeListCounts[i]);
+    for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_nodeListAndCollectionCounts); ++i)
+        ASSERT(!m_nodeListAndCollectionCounts[i]);
 
     clearDocumentScope();
 
@@ -3357,23 +3358,37 @@ void Document::setCSSTarget(Element* n)
         n->didAffectSelector(AffectedSelectorTarget);
 }
 
-void Document::registerNodeList(LiveNodeListBase* list)
+void Document::registerNodeList(LiveNodeList& list)
 {
-    if (list->hasIdNameCache())
-        m_nodeListCounts[InvalidateOnIdNameAttrChange]++;
-    m_nodeListCounts[list->invalidationType()]++;
-    if (list->isRootedAtDocument())
-        m_listsInvalidatedAtDocument.add(list);
+    m_nodeListAndCollectionCounts[list.invalidationType()]++;
+    if (list.isRootedAtDocument())
+        m_listsInvalidatedAtDocument.add(&list);
 }
 
-void Document::unregisterNodeList(LiveNodeListBase* list)
+void Document::unregisterNodeList(LiveNodeList& list)
 {
-    if (list->hasIdNameCache())
-        m_nodeListCounts[InvalidateOnIdNameAttrChange]--;
-    m_nodeListCounts[list->invalidationType()]--;
-    if (list->isRootedAtDocument()) {
-        ASSERT(m_listsInvalidatedAtDocument.contains(list));
-        m_listsInvalidatedAtDocument.remove(list);
+    m_nodeListAndCollectionCounts[list.invalidationType()]--;
+    if (list.isRootedAtDocument()) {
+        ASSERT(m_listsInvalidatedAtDocument.contains(&list));
+        m_listsInvalidatedAtDocument.remove(&list);
+    }
+}
+
+void Document::registerCollection(HTMLCollection& collection)
+{
+    m_nodeListAndCollectionCounts[InvalidateOnIdNameAttrChange]++;
+    m_nodeListAndCollectionCounts[collection.invalidationType()]++;
+    if (collection.isRootedAtDocument())
+        m_collectionsInvalidatedAtDocument.add(&collection);
+}
+
+void Document::unregisterCollection(HTMLCollection& collection)
+{
+    m_nodeListAndCollectionCounts[InvalidateOnIdNameAttrChange]--;
+    m_nodeListAndCollectionCounts[collection.invalidationType()]--;
+    if (collection.isRootedAtDocument()) {
+        ASSERT(m_collectionsInvalidatedAtDocument.contains(&collection));
+        m_collectionsInvalidatedAtDocument.remove(&collection);
     }
 }
 
@@ -4264,7 +4279,7 @@ bool Document::hasSVGRootNode() const
 
 PassRefPtr<HTMLCollection> Document::ensureCachedCollection(CollectionType type)
 {
-    return ensureRareData().ensureNodeLists().addCacheWithAtomicName<HTMLCollection>(*this, type);
+    return ensureRareData().ensureNodeLists().addCachedCollection<HTMLCollection>(*this, type);
 }
 
 PassRefPtr<HTMLCollection> Document::images()
@@ -4310,17 +4325,17 @@ PassRefPtr<HTMLCollection> Document::anchors()
 
 PassRefPtr<HTMLCollection> Document::all()
 {
-    return ensureRareData().ensureNodeLists().addCacheWithAtomicName<HTMLAllCollection>(*this, DocAll);
+    return ensureRareData().ensureNodeLists().addCachedCollection<HTMLAllCollection>(*this, DocAll);
 }
 
 PassRefPtr<HTMLCollection> Document::windowNamedItems(const AtomicString& name)
 {
-    return ensureRareData().ensureNodeLists().addCacheWithAtomicName<WindowNameCollection>(*this, WindowNamedItems, name);
+    return ensureRareData().ensureNodeLists().addCachedCollection<WindowNameCollection>(*this, WindowNamedItems, name);
 }
 
 PassRefPtr<HTMLCollection> Document::documentNamedItems(const AtomicString& name)
 {
-    return ensureRareData().ensureNodeLists().addCacheWithAtomicName<DocumentNameCollection>(*this, DocumentNamedItems, name);
+    return ensureRareData().ensureNodeLists().addCachedCollection<DocumentNameCollection>(*this, DocumentNamedItems, name);
 }
 
 void Document::finishedParsing()
index 680ceb9..fc3634a 100644 (file)
@@ -110,7 +110,7 @@ class HitTestResult;
 class IntPoint;
 class LayoutPoint;
 class LayoutRect;
-class LiveNodeListBase;
+class LiveNodeList;
 class JSNode;
 class Locale;
 class MediaCanStartListener;
@@ -697,10 +697,12 @@ public:
     void styleRecalcTimerFired(Timer<Document>*);
     void optimizedStyleSheetUpdateTimerFired(Timer<Document>*);
 
-    void registerNodeList(LiveNodeListBase*);
-    void unregisterNodeList(LiveNodeListBase*);
-    bool shouldInvalidateNodeListCaches(const QualifiedName* attrName = 0) const;
-    void invalidateNodeListCaches(const QualifiedName* attrName);
+    void registerNodeList(LiveNodeList&);
+    void unregisterNodeList(LiveNodeList&);
+    void registerCollection(HTMLCollection&);
+    void unregisterCollection(HTMLCollection&);
+    bool shouldInvalidateNodeListAndCollectionCaches(const QualifiedName* attrName = nullptr) const;
+    void invalidateNodeListAndCollectionCaches(const QualifiedName* attrName);
 
     void attachNodeIterator(NodeIterator*);
     void detachNodeIterator(NodeIterator*);
@@ -1415,8 +1417,10 @@ private:
 
     InheritedBool m_designMode;
 
-    HashSet<LiveNodeListBase*> m_listsInvalidatedAtDocument;
-    unsigned m_nodeListCounts[numNodeListInvalidationTypes];
+    HashSet<LiveNodeList*> m_listsInvalidatedAtDocument;
+    HashSet<HTMLCollection*> m_collectionsInvalidatedAtDocument;
+
+    unsigned m_nodeListAndCollectionCounts[numNodeListInvalidationTypes];
 
     RefPtr<XPathEvaluator> m_xpathEvaluator;
 
index 10461b3..0f53724 100644 (file)
@@ -1109,7 +1109,7 @@ void Element::attributeChanged(const QualifiedName& name, const AtomicString& ne
         shouldInvalidateStyle |= testShouldInvalidateStyle && isInShadowTree();
 
 
-    invalidateNodeListCachesInAncestors(&name, this);
+    invalidateNodeListAndCollectionCachesInAncestors(&name, this);
 
     // If there is currently no StyleResolver, we can't be sure that this attribute change won't affect style.
     shouldInvalidateStyle |= !styleResolver;
@@ -2949,19 +2949,19 @@ PassRefPtr<HTMLCollection> Element::ensureCachedHTMLCollection(CollectionType ty
 
     RefPtr<HTMLCollection> collection;
     if (type == TableRows) {
-        return ensureRareData().ensureNodeLists().addCacheWithAtomicName<HTMLTableRowsCollection>(toHTMLTableElement(*this), type);
+        return ensureRareData().ensureNodeLists().addCachedCollection<HTMLTableRowsCollection>(toHTMLTableElement(*this), type);
     } else if (type == SelectOptions) {
-        return ensureRareData().ensureNodeLists().addCacheWithAtomicName<HTMLOptionsCollection>(toHTMLSelectElement(*this), type);
+        return ensureRareData().ensureNodeLists().addCachedCollection<HTMLOptionsCollection>(toHTMLSelectElement(*this), type);
     } else if (type == FormControls) {
         ASSERT(hasTagName(formTag) || hasTagName(fieldsetTag));
-        return ensureRareData().ensureNodeLists().addCacheWithAtomicName<HTMLFormControlsCollection>(*this, type);
+        return ensureRareData().ensureNodeLists().addCachedCollection<HTMLFormControlsCollection>(*this, type);
     }
-    return ensureRareData().ensureNodeLists().addCacheWithAtomicName<HTMLCollection>(*this, type);
+    return ensureRareData().ensureNodeLists().addCachedCollection<HTMLCollection>(*this, type);
 }
 
 HTMLCollection* Element::cachedHTMLCollection(CollectionType type)
 {
-    return hasRareData() && rareData()->nodeLists() ? rareData()->nodeLists()->cacheWithAtomicName<HTMLCollection>(type) : 0;
+    return hasRareData() && rareData()->nodeLists() ? rareData()->nodeLists()->cachedCollection<HTMLCollection>(type) : 0;
 }
 
 IntSize Element::savedLayerScrollOffset() const
index 5eb34c8..1f6d7b8 100644 (file)
 #include "config.h"
 #include "LiveNodeList.h"
 
+#include "ClassNodeList.h"
 #include "Element.h"
+#include "ElementTraversal.h"
 #include "HTMLCollection.h"
+#include "TagNodeList.h"
 
 namespace WebCore {
 
-ContainerNode& LiveNodeListBase::rootNode() const
+ContainerNode& LiveNodeList::rootNode() const
 {
     if (isRootedAtDocument() && ownerNode().inDocument())
         return ownerNode().document();
@@ -36,28 +39,189 @@ ContainerNode& LiveNodeListBase::rootNode() const
     return ownerNode();
 }
 
-void LiveNodeListBase::invalidateCache() const
+template <class NodeListType>
+inline bool isMatchingElement(const NodeListType*, Element*);
+
+template <> inline bool isMatchingElement(const LiveNodeList* nodeList, Element* element)
 {
-    m_cachedElement = nullptr;
-    m_isLengthCacheValid = false;
-    m_isElementCacheValid = false;
-    m_isNameCacheValid = false;
-    m_isItemRefElementsCacheValid = false;
-    if (isNodeList(type()))
-        return;
+    return nodeList->nodeMatches(element);
+}
+
+template <> inline bool isMatchingElement(const HTMLTagNodeList* nodeList, Element* element)
+{
+    return nodeList->nodeMatchesInlined(element);
+}
+
+template <> inline bool isMatchingElement(const ClassNodeList* nodeList, Element* element)
+{
+    return nodeList->nodeMatchesInlined(element);
+}
+
+ALWAYS_INLINE Element* LiveNodeList::iterateForPreviousElement(Element* current) const
+{
+    ContainerNode& rootNode = this->rootNode();
+    for (; current; current = ElementTraversal::previous(current, &rootNode)) {
+        if (isMatchingElement(static_cast<const LiveNodeList*>(this), current))
+            return current;
+    }
+    return 0;
+}
+
+ALWAYS_INLINE Element* LiveNodeList::itemBefore(Element* previous) const
+{
+    Element* current;
+    if (LIKELY(!!previous)) // Without this LIKELY, length() and item() can be 10% slower.
+        current = ElementTraversal::previous(previous, &rootNode());
+    else
+        current = ElementTraversal::lastWithin(&rootNode());
 
-    const HTMLCollection* cacheBase = static_cast<const HTMLCollection*>(this);
-    cacheBase->m_idCache.clear();
-    cacheBase->m_nameCache.clear();
-    cacheBase->m_cachedElementsArrayOffset = 0;
+    return iterateForPreviousElement(current);
 }
 
-void LiveNodeListBase::invalidateIdNameCacheMaps() const
+template <class NodeListType>
+inline Element* firstMatchingElement(const NodeListType* nodeList, ContainerNode* root)
 {
-    ASSERT(hasIdNameCache());
-    const HTMLCollection* cacheBase = static_cast<const HTMLCollection*>(this);
-    cacheBase->m_idCache.clear();
-    cacheBase->m_nameCache.clear();
+    Element* element = ElementTraversal::firstWithin(root);
+    while (element && !isMatchingElement(nodeList, element))
+        element = ElementTraversal::next(element, root);
+    return element;
+}
+
+template <class NodeListType>
+inline Element* nextMatchingElement(const NodeListType* nodeList, Element* current, ContainerNode* root)
+{
+    do {
+        current = ElementTraversal::next(current, root);
+    } while (current && !isMatchingElement(nodeList, current));
+    return current;
+}
+
+template <class NodeListType>
+inline Element* traverseMatchingElementsForwardToOffset(const NodeListType* nodeList, unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root)
+{
+    ASSERT_WITH_SECURITY_IMPLICATION(currentOffset < offset);
+    while ((currentElement = nextMatchingElement(nodeList, currentElement, root))) {
+        if (++currentOffset == offset)
+            return currentElement;
+    }
+    return 0;
+}
+
+inline Element* LiveNodeList::traverseLiveNodeListFirstElement(ContainerNode* root) const
+{
+    if (type() == HTMLTagNodeListType)
+        return firstMatchingElement(static_cast<const HTMLTagNodeList*>(this), root);
+    if (type() == ClassNodeListType)
+        return firstMatchingElement(static_cast<const ClassNodeList*>(this), root);
+    return firstMatchingElement(static_cast<const LiveNodeList*>(this), root);
+}
+
+inline Element* LiveNodeList::traverseLiveNodeListForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root) const
+{
+    if (type() == HTMLTagNodeListType)
+        return traverseMatchingElementsForwardToOffset(static_cast<const HTMLTagNodeList*>(this), offset, currentElement, currentOffset, root);
+    if (type() == ClassNodeListType)
+        return traverseMatchingElementsForwardToOffset(static_cast<const ClassNodeList*>(this), offset, currentElement, currentOffset, root);
+    return traverseMatchingElementsForwardToOffset(static_cast<const LiveNodeList*>(this), offset, currentElement, currentOffset, root);
+}
+
+bool ALWAYS_INLINE LiveNodeList::isLastItemCloserThanLastOrCachedItem(unsigned offset) const
+{
+    ASSERT(isLengthCacheValid());
+    unsigned distanceFromLastItem = cachedLength() - offset;
+    if (!isElementCacheValid())
+        return distanceFromLastItem < offset;
+
+    return cachedElementOffset() < offset && distanceFromLastItem < offset - cachedElementOffset();
+}
+    
+bool ALWAYS_INLINE LiveNodeList::isFirstItemCloserThanCachedItem(unsigned offset) const
+{
+    ASSERT(isElementCacheValid());
+    if (cachedElementOffset() < offset)
+        return false;
+
+    unsigned distanceFromCachedItem = cachedElementOffset() - offset;
+    return offset < distanceFromCachedItem;
+}
+
+unsigned LiveNodeList::length() const
+{
+    if (isLengthCacheValid())
+        return cachedLength();
+
+    item(UINT_MAX);
+    ASSERT(isLengthCacheValid());
+    
+    return cachedLength();
+}
+
+Node* LiveNodeList::item(unsigned offset) const
+{
+    if (isElementCacheValid() && cachedElementOffset() == offset)
+        return cachedElement();
+
+    if (isLengthCacheValid() && cachedLength() <= offset)
+        return 0;
+
+    ContainerNode& root = rootNode();
+
+    if (isLengthCacheValid() && isLastItemCloserThanLastOrCachedItem(offset)) {
+        Element* lastItem = itemBefore(0);
+        ASSERT(lastItem);
+        setCachedElement(*lastItem, cachedLength() - 1);
+    } else if (!isElementCacheValid() || isFirstItemCloserThanCachedItem(offset)) {
+        Element* firstItem = traverseLiveNodeListFirstElement(&root);
+        if (!firstItem) {
+            setLengthCache(0);
+            return 0;
+        }
+        setCachedElement(*firstItem, 0);
+        ASSERT(!cachedElementOffset());
+    }
+
+    if (cachedElementOffset() == offset)
+        return cachedElement();
+
+    return elementBeforeOrAfterCachedElement(offset, &root);
+}
+
+inline Element* LiveNodeList::elementBeforeOrAfterCachedElement(unsigned offset, ContainerNode* root) const
+{
+    unsigned currentOffset = cachedElementOffset();
+    Element* currentItem = cachedElement();
+    ASSERT(currentItem);
+    ASSERT(currentOffset != offset);
+
+    if (offset < cachedElementOffset()) {
+        while ((currentItem = itemBefore(currentItem))) {
+            ASSERT(currentOffset);
+            currentOffset--;
+            if (currentOffset == offset) {
+                setCachedElement(*currentItem, currentOffset);
+                return currentItem;
+            }
+        }
+        ASSERT_NOT_REACHED();
+        return 0;
+    }
+
+    currentItem = traverseLiveNodeListForwardToOffset(offset, currentItem, currentOffset, root);
+
+    if (!currentItem) {
+        // Did not find the item. On plus side, we now know the length.
+        setLengthCache(currentOffset + 1);
+        return 0;
+    }
+    setCachedElement(*currentItem, currentOffset);
+    return currentItem;
+}
+
+void LiveNodeList::invalidateCache() const
+{
+    m_cachedElement = nullptr;
+    m_isLengthCacheValid = false;
+    m_isElementCacheValid = false;
 }
 
 Node* LiveNodeList::namedItem(const AtomicString& elementId) const
index 42b33c6..28e7cad 100644 (file)
@@ -37,40 +37,41 @@ class Element;
 
 enum NodeListRootType {
     NodeListIsRootedAtNode,
-    NodeListIsRootedAtDocument,
-    NodeListIsRootedAtDocumentIfOwnerHasItemrefAttr,
+    NodeListIsRootedAtDocument
 };
 
-class LiveNodeListBase : public NodeList {
+static bool shouldInvalidateTypeOnAttributeChange(NodeListInvalidationType, const QualifiedName&);
+
+class LiveNodeList : public NodeList {
 public:
-    enum ItemAfterOverrideType {
-        OverridesItemAfter,
-        DoesNotOverrideItemAfter,
+    enum Type {
+        ClassNodeListType,
+        NameNodeListType,
+        TagNodeListType,
+        HTMLTagNodeListType,
+        RadioNodeListType,
+        LabelsNodeListType,
     };
 
-    LiveNodeListBase(ContainerNode& ownerNode, NodeListRootType rootType, NodeListInvalidationType invalidationType,
-        bool shouldOnlyIncludeDirectChildren, CollectionType collectionType, ItemAfterOverrideType itemAfterOverrideType)
+    LiveNodeList(ContainerNode& ownerNode, Type type, NodeListInvalidationType invalidationType, NodeListRootType rootType = NodeListIsRootedAtNode)
         : m_ownerNode(ownerNode)
         , m_cachedElement(nullptr)
         , m_isLengthCacheValid(false)
         , m_isElementCacheValid(false)
         , m_rootType(rootType)
         , m_invalidationType(invalidationType)
-        , m_shouldOnlyIncludeDirectChildren(shouldOnlyIncludeDirectChildren)
-        , m_isNameCacheValid(false)
-        , m_collectionType(collectionType)
-        , m_overridesItemAfter(itemAfterOverrideType == OverridesItemAfter)
-        , m_isItemRefElementsCacheValid(false)
+        , m_type(type)
     {
         ASSERT(m_rootType == static_cast<unsigned>(rootType));
         ASSERT(m_invalidationType == static_cast<unsigned>(invalidationType));
-        ASSERT(m_collectionType == static_cast<unsigned>(collectionType));
-        ASSERT(!m_overridesItemAfter || !isNodeList(collectionType));
+        ASSERT(m_type == static_cast<unsigned>(type));
 
         document().registerNodeList(this);
     }
+    virtual Node* namedItem(const AtomicString&) const OVERRIDE;
+    virtual bool nodeMatches(Element*) const = 0;
 
-    virtual ~LiveNodeListBase()
+    virtual ~LiveNodeList()
     {
         document().unregisterNodeList(this);
     }
@@ -79,27 +80,20 @@ public:
     virtual unsigned length() const OVERRIDE;
     virtual Node* item(unsigned offset) const OVERRIDE;
 
-    ALWAYS_INLINE bool hasIdNameCache() const { return !isNodeList(type()); }
-    ALWAYS_INLINE bool isRootedAtDocument() const { return m_rootType == NodeListIsRootedAtDocument || m_rootType == NodeListIsRootedAtDocumentIfOwnerHasItemrefAttr; }
+    ALWAYS_INLINE bool isRootedAtDocument() const { return m_rootType == NodeListIsRootedAtDocument; }
     ALWAYS_INLINE NodeListInvalidationType invalidationType() const { return static_cast<NodeListInvalidationType>(m_invalidationType); }
-    ALWAYS_INLINE CollectionType type() const { return static_cast<CollectionType>(m_collectionType); }
+    ALWAYS_INLINE Type type() const { return static_cast<Type>(m_type); }
     ContainerNode& ownerNode() const { return const_cast<ContainerNode&>(m_ownerNode.get()); }
     ALWAYS_INLINE void invalidateCache(const QualifiedName* attrName) const
     {
         if (!attrName || shouldInvalidateTypeOnAttributeChange(invalidationType(), *attrName))
             invalidateCache();
-        else if (hasIdNameCache() && (*attrName == HTMLNames::idAttr || *attrName == HTMLNames::nameAttr))
-            invalidateIdNameCacheMaps();
     }
     void invalidateCache() const;
-    void invalidateIdNameCacheMaps() const;
-
-    static bool shouldInvalidateTypeOnAttributeChange(NodeListInvalidationType, const QualifiedName&);
 
 protected:
     Document& document() const { return m_ownerNode->document(); }
     ContainerNode& rootNode() const;
-    bool overridesItemAfter() const { return m_overridesItemAfter; }
 
     ALWAYS_INLINE bool isElementCacheValid() const { return m_isElementCacheValid; }
     ALWAYS_INLINE Element* cachedElement() const { return m_cachedElement; }
@@ -118,19 +112,12 @@ protected:
         m_cachedElementOffset = offset;
         m_isElementCacheValid = true;
     }
-    void setCachedElement(Element&, unsigned offset, unsigned elementsArrayOffset) const;
-
-    ALWAYS_INLINE bool isItemRefElementsCacheValid() const { return m_isItemRefElementsCacheValid; }
-    ALWAYS_INLINE void setItemRefElementsCacheValid() const { m_isItemRefElementsCacheValid = true; }
 
     ALWAYS_INLINE NodeListRootType rootType() const { return static_cast<NodeListRootType>(m_rootType); }
 
-    bool hasNameCache() const { return m_isNameCacheValid; }
-    void setHasNameCache() const { m_isNameCacheValid = true; }
-
-    bool shouldOnlyIncludeDirectChildren() const { return m_shouldOnlyIncludeDirectChildren; }
-
 private:
+    virtual bool isLiveNodeList() const OVERRIDE { return true; }
+
     Element* elementBeforeOrAfterCachedElement(unsigned offset, ContainerNode* root) const;
     Element* traverseLiveNodeListFirstElement(ContainerNode* root) const;
     Element* traverseLiveNodeListForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root) const;
@@ -147,16 +134,10 @@ private:
     mutable unsigned m_isElementCacheValid : 1;
     const unsigned m_rootType : 2;
     const unsigned m_invalidationType : 4;
-    const unsigned m_shouldOnlyIncludeDirectChildren : 1;
-
-    // From HTMLCollection
-    mutable unsigned m_isNameCacheValid : 1;
-    const unsigned m_collectionType : 5;
-    const unsigned m_overridesItemAfter : 1;
-    mutable unsigned m_isItemRefElementsCacheValid : 1;
+    const unsigned m_type : 5;
 };
 
-ALWAYS_INLINE bool LiveNodeListBase::shouldInvalidateTypeOnAttributeChange(NodeListInvalidationType type, const QualifiedName& attrName)
+ALWAYS_INLINE bool shouldInvalidateTypeOnAttributeChange(NodeListInvalidationType type, const QualifiedName& attrName)
 {
     switch (type) {
     case InvalidateOnClassAttrChange:
@@ -180,19 +161,6 @@ ALWAYS_INLINE bool LiveNodeListBase::shouldInvalidateTypeOnAttributeChange(NodeL
     return false;
 }
 
-class LiveNodeList : public LiveNodeListBase {
-public:
-    LiveNodeList(ContainerNode& ownerNode, CollectionType collectionType, NodeListInvalidationType invalidationType, NodeListRootType rootType = NodeListIsRootedAtNode)
-        : LiveNodeListBase(ownerNode, rootType, invalidationType, /*shouldOnlyIncludeDirectChildren*/ false, collectionType, DoesNotOverrideItemAfter)
-    { }
-
-    virtual Node* namedItem(const AtomicString&) const OVERRIDE;
-    virtual bool nodeMatches(Element*) const = 0;
-
-private:
-    virtual bool isLiveNodeList() const OVERRIDE { return true; }
-};
-
 } // namespace WebCore
 
 #endif // LiveNodeList_h
index 929b914..e26abac 100644 (file)
@@ -37,7 +37,7 @@ NameNodeList::NameNodeList(ContainerNode& rootNode, const AtomicString& name)
 
 NameNodeList::~NameNodeList()
 {
-    ownerNode().nodeLists()->removeCacheWithAtomicName(this, NameNodeListType, m_name);
+    ownerNode().nodeLists()->removeCacheWithAtomicName(this, m_name);
 } 
 
 bool NameNodeList::nodeMatches(Element* testNode) const
index 9b71cf9..7fce6d6 100644 (file)
@@ -33,7 +33,7 @@ namespace WebCore {
 // NodeList which lists all Nodes in a Element with a given "name" attribute
 class NameNodeList : public LiveNodeList {
 public:
-    static PassRefPtr<NameNodeList> create(ContainerNode& rootNode, CollectionType type, const AtomicString& name)
+    static PassRefPtr<NameNodeList> create(ContainerNode& rootNode, Type type, const AtomicString& name)
     {
         ASSERT_UNUSED(type, type == NameNodeListType);
         return adoptRef(new NameNodeList(rootNode, name));
index 30885d1..608b521 100644 (file)
@@ -48,6 +48,7 @@
 #include "EventHandler.h"
 #include "FlowThreadController.h"
 #include "FrameView.h"
+#include "HTMLCollection.h"
 #include "HTMLElement.h"
 #include "HTMLImageElement.h"
 #include "HTMLStyleElement.h"
@@ -689,7 +690,7 @@ unsigned Node::nodeIndex() const
 template<unsigned type>
 bool shouldInvalidateNodeListCachesForAttr(const unsigned nodeListCounts[], const QualifiedName& attrName)
 {
-    if (nodeListCounts[type] && LiveNodeListBase::shouldInvalidateTypeOnAttributeChange(static_cast<NodeListInvalidationType>(type), attrName))
+    if (nodeListCounts[type] && shouldInvalidateTypeOnAttributeChange(static_cast<NodeListInvalidationType>(type), attrName))
         return true;
     return shouldInvalidateNodeListCachesForAttr<type + 1>(nodeListCounts, attrName);
 }
@@ -700,27 +701,28 @@ bool shouldInvalidateNodeListCachesForAttr<numNodeListInvalidationTypes>(const u
     return false;
 }
 
-bool Document::shouldInvalidateNodeListCaches(const QualifiedName* attrName) const
+bool Document::shouldInvalidateNodeListAndCollectionCaches(const QualifiedName* attrName) const
 {
     if (attrName)
-        return shouldInvalidateNodeListCachesForAttr<DoNotInvalidateOnAttributeChanges + 1>(m_nodeListCounts, *attrName);
+        return shouldInvalidateNodeListCachesForAttr<DoNotInvalidateOnAttributeChanges + 1>(m_nodeListAndCollectionCounts, *attrName);
 
     for (int type = 0; type < numNodeListInvalidationTypes; type++) {
-        if (m_nodeListCounts[type])
+        if (m_nodeListAndCollectionCounts[type])
             return true;
     }
 
     return false;
 }
 
-void Document::invalidateNodeListCaches(const QualifiedName* attrName)
+void Document::invalidateNodeListAndCollectionCaches(const QualifiedName* attrName)
 {
-    HashSet<LiveNodeListBase*>::iterator end = m_listsInvalidatedAtDocument.end();
-    for (HashSet<LiveNodeListBase*>::iterator it = m_listsInvalidatedAtDocument.begin(); it != end; ++it)
+    for (HashSet<LiveNodeList*>::iterator it = m_listsInvalidatedAtDocument.begin(), end = m_listsInvalidatedAtDocument.end(); it != end; ++it)
+        (*it)->invalidateCache(attrName);
+    for (HashSet<HTMLCollection*>::iterator it = m_collectionsInvalidatedAtDocument.begin(), end = m_collectionsInvalidatedAtDocument.end(); it != end; ++it)
         (*it)->invalidateCache(attrName);
 }
 
-void Node::invalidateNodeListCachesInAncestors(const QualifiedName* attrName, Element* attributeOwnerElement)
+void Node::invalidateNodeListAndCollectionCachesInAncestors(const QualifiedName* attrName, Element* attributeOwnerElement)
 {
     if (hasRareData() && (!attrName || isAttributeNode())) {
         if (NodeListsNodeData* lists = rareData()->nodeLists())
@@ -731,10 +733,10 @@ void Node::invalidateNodeListCachesInAncestors(const QualifiedName* attrName, El
     if (attrName && !attributeOwnerElement)
         return;
 
-    if (!document().shouldInvalidateNodeListCaches(attrName))
+    if (!document().shouldInvalidateNodeListAndCollectionCaches(attrName))
         return;
 
-    document().invalidateNodeListCaches(attrName);
+    document().invalidateNodeListAndCollectionCaches(attrName);
 
     for (Node* node = this; node; node = node->parentNode()) {
         if (!node->hasRareData())
@@ -1672,19 +1674,19 @@ void Node::showTreeForThisAcrossFrame() const
 
 void NodeListsNodeData::invalidateCaches(const QualifiedName* attrName)
 {
-    NodeListAtomicNameCacheMap::const_iterator atomicNameCacheEnd = m_atomicNameCaches.end();
-    for (NodeListAtomicNameCacheMap::const_iterator it = m_atomicNameCaches.begin(); it != atomicNameCacheEnd; ++it)
+    for (auto it = m_atomicNameCaches.begin(), end = m_atomicNameCaches.end(); it != end; ++it)
+        it->value->invalidateCache(attrName);
+
+    for (auto it = m_nameCaches.begin(), end = m_nameCaches.end(); it != end; ++it)
         it->value->invalidateCache(attrName);
 
-    NodeListNameCacheMap::const_iterator nameCacheEnd = m_nameCaches.end();
-    for (NodeListNameCacheMap::const_iterator it = m_nameCaches.begin(); it != nameCacheEnd; ++it)
+    for (auto it = m_cachedCollections.begin(), end = m_cachedCollections.end(); it != end; ++it)
         it->value->invalidateCache(attrName);
 
     if (attrName)
         return;
 
-    TagNodeListCacheNS::iterator tagCacheEnd = m_tagNodeListCacheNS.end();
-    for (TagNodeListCacheNS::iterator it = m_tagNodeListCacheNS.begin(); it != tagCacheEnd; ++it)
+    for (auto it = m_tagNodeListCacheNS.begin(), end = m_tagNodeListCacheNS.end(); it != end; ++it)
         it->value->invalidateCache();
 }
 
index a7770d5..3adf15d 100644 (file)
@@ -495,7 +495,7 @@ public:
     void showTreeForThisAcrossFrame() const;
 #endif
 
-    void invalidateNodeListCachesInAncestors(const QualifiedName* attrName = 0, Element* attributeOwnerElement = 0);
+    void invalidateNodeListAndCollectionCachesInAncestors(const QualifiedName* attrName = 0, Element* attributeOwnerElement = 0);
     NodeListsNodeData* nodeLists();
     void clearNodeLists();
 
index a50f1c0..bb82bbb 100644 (file)
@@ -23,7 +23,9 @@
 #define NodeRareData_h
 
 #include "ChildNodeList.h"
+#include "ClassNodeList.h"
 #include "DOMSettableTokenList.h"
+#include "HTMLCollection.h"
 #include "HTMLNames.h"
 #include "LiveNodeList.h"
 #include "MutationObserver.h"
@@ -101,79 +103,91 @@ public:
         static const bool safeToCompareToEmptyOrDeleted = DefaultHash<StringType>::Hash::safeToCompareToEmptyOrDeleted;
     };
 
-    typedef HashMap<std::pair<unsigned char, AtomicString>, LiveNodeListBase*, NodeListCacheMapEntryHash<AtomicString>> NodeListAtomicNameCacheMap;
-    typedef HashMap<std::pair<unsigned char, String>, LiveNodeListBase*, NodeListCacheMapEntryHash<String>> NodeListNameCacheMap;
+    typedef HashMap<std::pair<unsigned char, AtomicString>, LiveNodeList*, NodeListCacheMapEntryHash<AtomicString>> NodeListAtomicNameCacheMap;
+    typedef HashMap<std::pair<unsigned char, String>, LiveNodeList*, NodeListCacheMapEntryHash<String>> NodeListNameCacheMap;
+    typedef HashMap<std::pair<unsigned char, AtomicString>, HTMLCollection*, NodeListCacheMapEntryHash<AtomicString>> CollectionCacheMap;
     typedef HashMap<QualifiedName, TagNodeList*> TagNodeListCacheNS;
 
     template<typename T, typename ContainerType>
-    PassRefPtr<T> addCacheWithAtomicName(ContainerType& container, CollectionType collectionType, const AtomicString& name)
+    PassRefPtr<T> addCacheWithAtomicName(ContainerType& container, LiveNodeList::Type type, const AtomicString& name)
     {
-        NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.add(namedNodeListKey(collectionType, name), nullptr);
+        NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.add(namedNodeListKey(type, name), nullptr);
         if (!result.isNewEntry)
             return static_cast<T*>(result.iterator->value);
 
-        RefPtr<T> list = T::create(container, collectionType, name);
+        RefPtr<T> list = T::create(container, type, name);
         result.iterator->value = list.get();
         return list.release();
     }
 
-    // FIXME: This function should be renamed since it doesn't have an atomic name.
-    template<typename T, typename ContainerType>
-    PassRefPtr<T> addCacheWithAtomicName(ContainerType& container, CollectionType collectionType)
+    template<typename T>
+    PassRefPtr<T> addCacheWithName(ContainerNode& node, LiveNodeList::Type type, const String& name)
     {
-        NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.add(namedNodeListKey(collectionType, starAtom), nullptr);
+        NodeListNameCacheMap::AddResult result = m_nameCaches.add(namedNodeListKey(type, name), nullptr);
         if (!result.isNewEntry)
             return static_cast<T*>(result.iterator->value);
 
-        RefPtr<T> list = T::create(container, collectionType);
+        RefPtr<T> list = T::create(node, name);
         result.iterator->value = list.get();
         return list.release();
     }
 
-    template<typename T>
-    T* cacheWithAtomicName(CollectionType collectionType)
+    PassRefPtr<TagNodeList> addCacheWithQualifiedName(ContainerNode& node, const AtomicString& namespaceURI, const AtomicString& localName)
     {
-        return static_cast<T*>(m_atomicNameCaches.get(namedNodeListKey(collectionType, starAtom)));
+        QualifiedName name(nullAtom, localName, namespaceURI);
+        TagNodeListCacheNS::AddResult result = m_tagNodeListCacheNS.add(name, nullptr);
+        if (!result.isNewEntry)
+            return result.iterator->value;
+
+        RefPtr<TagNodeList> list = TagNodeList::create(node, namespaceURI, localName);
+        result.iterator->value = list.get();
+        return list.release();
     }
 
-    template<typename T>
-    PassRefPtr<T> addCacheWithName(ContainerNode& node, CollectionType collectionType, const String& name)
+    template<typename T, typename ContainerType>
+    PassRefPtr<T> addCachedCollection(ContainerType& container, CollectionType collectionType, const AtomicString& name)
     {
-        NodeListNameCacheMap::AddResult result = m_nameCaches.add(namedNodeListKey(collectionType, name), nullptr);
+        CollectionCacheMap::AddResult result = m_cachedCollections.add(namedCollectionKey(collectionType, name), nullptr);
         if (!result.isNewEntry)
             return static_cast<T*>(result.iterator->value);
 
-        RefPtr<T> list = T::create(node, name);
+        RefPtr<T> list = T::create(container, collectionType, name);
         result.iterator->value = list.get();
         return list.release();
     }
 
-    PassRefPtr<TagNodeList> addCacheWithQualifiedName(ContainerNode& node, const AtomicString& namespaceURI, const AtomicString& localName)
+    template<typename T, typename ContainerType>
+    PassRefPtr<T> addCachedCollection(ContainerType& container, CollectionType collectionType)
     {
-        QualifiedName name(nullAtom, localName, namespaceURI);
-        TagNodeListCacheNS::AddResult result = m_tagNodeListCacheNS.add(name, nullptr);
+        CollectionCacheMap::AddResult result = m_cachedCollections.add(namedCollectionKey(collectionType, starAtom), nullptr);
         if (!result.isNewEntry)
-            return result.iterator->value;
+            return static_cast<T*>(result.iterator->value);
 
-        RefPtr<TagNodeList> list = TagNodeList::create(node, namespaceURI, localName);
+        RefPtr<T> list = T::create(container, collectionType);
         result.iterator->value = list.get();
         return list.release();
     }
 
-    void removeCacheWithAtomicName(LiveNodeListBase* list, CollectionType collectionType, const AtomicString& name = starAtom)
+    template<typename T>
+    T* cachedCollection(CollectionType collectionType)
+    {
+        return static_cast<T*>(m_cachedCollections.get(namedCollectionKey(collectionType, starAtom)));
+    }
+
+    void removeCacheWithAtomicName(LiveNodeList* list, const AtomicString& name = starAtom)
     {
-        ASSERT(list == m_atomicNameCaches.get(namedNodeListKey(collectionType, name)));
+        ASSERT(list == m_atomicNameCaches.get(namedNodeListKey(list->type(), name)));
         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
             return;
-        m_atomicNameCaches.remove(namedNodeListKey(collectionType, name));
+        m_atomicNameCaches.remove(namedNodeListKey(list->type(), name));
     }
 
-    void removeCacheWithName(LiveNodeListBase* list, CollectionType collectionType, const String& name)
+    void removeCacheWithName(LiveNodeList* list, const String& name)
     {
-        ASSERT(list == m_nameCaches.get(namedNodeListKey(collectionType, name)));
+        ASSERT(list == m_nameCaches.get(namedNodeListKey(list->type(), name)));
         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
             return;
-        m_nameCaches.remove(namedNodeListKey(collectionType, name));
+        m_nameCaches.remove(namedNodeListKey(list->type(), name));
     }
 
     void removeCacheWithQualifiedName(LiveNodeList* list, const AtomicString& namespaceURI, const AtomicString& localName)
@@ -185,6 +199,14 @@ public:
         m_tagNodeListCacheNS.remove(name);
     }
 
+    void removeCachedCollection(HTMLCollection* collection, const AtomicString& name = starAtom)
+    {
+        ASSERT(collection == m_cachedCollections.get(namedCollectionKey(collection->type(), name)));
+        if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(collection->ownerNode()))
+            return;
+        m_cachedCollections.remove(namedCollectionKey(collection->type(), name));
+    }
+
     static PassOwnPtr<NodeListsNodeData> create()
     {
         return adoptPtr(new NodeListsNodeData);
@@ -193,7 +215,7 @@ public:
     void invalidateCaches(const QualifiedName* attrName = 0);
     bool isEmpty() const
     {
-        return m_atomicNameCaches.isEmpty() && m_nameCaches.isEmpty() && m_tagNodeListCacheNS.isEmpty();
+        return m_atomicNameCaches.isEmpty() && m_nameCaches.isEmpty() && m_cachedCollections.isEmpty() && m_tagNodeListCacheNS.isEmpty();
     }
 
     void adoptTreeScope()
@@ -206,27 +228,30 @@ public:
         invalidateCaches();
 
         if (oldDocument != newDocument) {
-            NodeListAtomicNameCacheMap::const_iterator atomicNameCacheEnd = m_atomicNameCaches.end();
-            for (NodeListAtomicNameCacheMap::const_iterator it = m_atomicNameCaches.begin(); it != atomicNameCacheEnd; ++it) {
-                LiveNodeListBase* list = it->value;
+            for (auto it = m_atomicNameCaches.begin(), end = m_atomicNameCaches.end(); it != end; ++it) {
+                LiveNodeList* list = it->value;
                 oldDocument->unregisterNodeList(list);
                 newDocument->registerNodeList(list);
             }
 
-            NodeListNameCacheMap::const_iterator nameCacheEnd = m_nameCaches.end();
-            for (NodeListNameCacheMap::const_iterator it = m_nameCaches.begin(); it != nameCacheEnd; ++it) {
-                LiveNodeListBase* list = it->value;
+            for (auto it = m_nameCaches.begin(), end = m_nameCaches.end(); it != end; ++it) {
+                LiveNodeList* list = it->value;
                 oldDocument->unregisterNodeList(list);
                 newDocument->registerNodeList(list);
             }
 
-            TagNodeListCacheNS::const_iterator tagEnd = m_tagNodeListCacheNS.end();
-            for (TagNodeListCacheNS::const_iterator it = m_tagNodeListCacheNS.begin(); it != tagEnd; ++it) {
-                LiveNodeListBase* list = it->value;
+            for (auto it = m_tagNodeListCacheNS.begin(), end = m_tagNodeListCacheNS.end(); it != end; ++it) {
+                LiveNodeList* list = it->value;
                 ASSERT(!list->isRootedAtDocument());
                 oldDocument->unregisterNodeList(list);
                 newDocument->registerNodeList(list);
             }
+
+            for (auto it = m_cachedCollections.begin(), end = m_cachedCollections.end(); it != end; ++it) {
+                HTMLCollection* collection = it->value;
+                oldDocument->unregisterCollection(collection);
+                newDocument->registerCollection(collection);
+            }
         }
     }
 
@@ -236,12 +261,12 @@ private:
         , m_emptyChildNodeList(nullptr)
     { }
 
-    std::pair<unsigned char, AtomicString> namedNodeListKey(CollectionType type, const AtomicString& name)
+    std::pair<unsigned char, AtomicString> namedCollectionKey(CollectionType type, const AtomicString& name)
     {
         return std::pair<unsigned char, AtomicString>(type, name);
     }
 
-    std::pair<unsigned char, String> namedNodeListKey(CollectionType type, const String& name)
+    std::pair<unsigned char, String> namedNodeListKey(LiveNodeList::Type type, const String& name)
     {
         return std::pair<unsigned char, String>(type, name);
     }
@@ -255,6 +280,7 @@ private:
     NodeListAtomicNameCacheMap m_atomicNameCaches;
     NodeListNameCacheMap m_nameCaches;
     TagNodeListCacheNS m_tagNodeListCacheNS;
+    CollectionCacheMap m_cachedCollections;
 };
 
 class NodeMutationObserverData {
@@ -319,7 +345,8 @@ private:
 inline bool NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node& ownerNode)
 {
     ASSERT(ownerNode.nodeLists() == this);
-    if ((m_childNodeList ? 1 : 0) + (m_emptyChildNodeList ? 1 : 0) + m_atomicNameCaches.size() + m_nameCaches.size() + m_tagNodeListCacheNS.size() != 1)
+    if ((m_childNodeList ? 1 : 0) + (m_emptyChildNodeList ? 1 : 0) + m_atomicNameCaches.size() + m_nameCaches.size()
+        + m_tagNodeListCacheNS.size() + m_cachedCollections.size() != 1)
         return false;
     ownerNode.clearNodeLists();
     return true;
index 358bef1..9f4a4f4 100644 (file)
@@ -28,7 +28,7 @@
 
 namespace WebCore {
 
-TagNodeList::TagNodeList(ContainerNode& rootNode, CollectionType type, const AtomicString& namespaceURI, const AtomicString& localName)
+TagNodeList::TagNodeList(ContainerNode& rootNode, Type type, const AtomicString& namespaceURI, const AtomicString& localName)
     : LiveNodeList(rootNode, type, DoNotInvalidateOnAttributeChanges)
     , m_namespaceURI(namespaceURI)
     , m_localName(localName)
@@ -39,7 +39,7 @@ TagNodeList::TagNodeList(ContainerNode& rootNode, CollectionType type, const Ato
 TagNodeList::~TagNodeList()
 {
     if (m_namespaceURI == starAtom)
-        ownerNode().nodeLists()->removeCacheWithAtomicName(this, type(), m_localName);
+        ownerNode().nodeLists()->removeCacheWithAtomicName(this, m_localName);
     else
         ownerNode().nodeLists()->removeCacheWithQualifiedName(this, m_namespaceURI, m_localName);
 }
index 2a724d5..5098d08 100644 (file)
@@ -39,7 +39,7 @@ public:
         return adoptRef(new TagNodeList(rootNode, TagNodeListType, namespaceURI, localName));
     }
 
-    static PassRefPtr<TagNodeList> create(ContainerNode& rootNode, CollectionType type, const AtomicString& localName)
+    static PassRefPtr<TagNodeList> create(ContainerNode& rootNode, Type type, const AtomicString& localName)
     {
         ASSERT_UNUSED(type, type == TagNodeListType);
         return adoptRef(new TagNodeList(rootNode, TagNodeListType, starAtom, localName));
@@ -48,7 +48,7 @@ public:
     virtual ~TagNodeList();
 
 protected:
-    TagNodeList(ContainerNode& rootNode, CollectionType, const AtomicString& namespaceURI, const AtomicString& localName);
+    TagNodeList(ContainerNode& rootNode, Type, const AtomicString& namespaceURI, const AtomicString& localName);
 
     virtual bool nodeMatches(Element*) const OVERRIDE;
 
@@ -58,7 +58,7 @@ protected:
 
 class HTMLTagNodeList : public TagNodeList {
 public:
-    static PassRefPtr<HTMLTagNodeList> create(ContainerNode& rootNode, CollectionType type, const AtomicString& localName)
+    static PassRefPtr<HTMLTagNodeList> create(ContainerNode& rootNode, Type type, const AtomicString& localName)
     {
         ASSERT_UNUSED(type, type == HTMLTagNodeListType);
         return adoptRef(new HTMLTagNodeList(rootNode, localName));
index 3429e39..5130af9 100644 (file)
@@ -50,24 +50,9 @@ enum CollectionType {
     SelectedOptions,
     DataListOptions,
     MapAreas,
-    FormControls,
-
-    // Live NodeList.
-    ClassNodeListType,
-    NameNodeListType,
-    TagNodeListType,
-    HTMLTagNodeListType,
-    RadioNodeListType,
-    LabelsNodeListType,
+    FormControls
 };
 
-static const CollectionType FirstNodeListType = ClassNodeListType;
-
-inline bool isNodeList(CollectionType type)
-{
-    return type >= FirstNodeListType;
-}
-
 } // namespace
 
 #endif
index 5d13ebc..25dd01e 100644 (file)
 #include "config.h"
 #include "HTMLCollection.h"
 
-#include "ClassNodeList.h"
 #include "ElementTraversal.h"
 #include "HTMLDocument.h"
 #include "HTMLNameCollection.h"
 #include "HTMLNames.h"
 #include "HTMLObjectElement.h"
 #include "HTMLOptionElement.h"
-#include "NodeList.h"
 #include "NodeRareData.h"
 
 namespace WebCore {
@@ -62,13 +60,6 @@ static bool shouldOnlyIncludeDirectChildren(CollectionType type)
     case TSectionRows:
     case TableTBodies:
         return true;
-    case ClassNodeListType:
-    case NameNodeListType:
-    case TagNodeListType:
-    case HTMLTagNodeListType:
-    case RadioNodeListType:
-    case LabelsNodeListType:
-        break;
     }
     ASSERT_NOT_REACHED();
     return false;
@@ -99,13 +90,6 @@ static NodeListRootType rootTypeFromCollectionType(CollectionType type)
     case DataListOptions:
     case MapAreas:
         return NodeListIsRootedAtNode;
-    case ClassNodeListType:
-    case NameNodeListType:
-    case TagNodeListType:
-    case HTMLTagNodeListType:
-    case RadioNodeListType:
-    case LabelsNodeListType:
-        break;
     }
     ASSERT_NOT_REACHED();
     return NodeListIsRootedAtNode;
@@ -142,22 +126,29 @@ static NodeListInvalidationType invalidationTypeExcludingIdAndNameAttributes(Col
         return InvalidateOnIdNameAttrChange;
     case FormControls:
         return InvalidateForFormControls;
-    case ClassNodeListType:
-    case NameNodeListType:
-    case TagNodeListType:
-    case HTMLTagNodeListType:
-    case RadioNodeListType:
-    case LabelsNodeListType:
-        break;
     }
     ASSERT_NOT_REACHED();
     return DoNotInvalidateOnAttributeChanges;
 }
 
 HTMLCollection::HTMLCollection(ContainerNode& ownerNode, CollectionType type, ItemAfterOverrideType itemAfterOverrideType)
-    : LiveNodeListBase(ownerNode, rootTypeFromCollectionType(type), invalidationTypeExcludingIdAndNameAttributes(type),
-        WebCore::shouldOnlyIncludeDirectChildren(type), type, itemAfterOverrideType)
+    : m_ownerNode(ownerNode)
+    , m_cachedElement(nullptr)
+    , m_isLengthCacheValid(false)
+    , m_isElementCacheValid(false)
+    , m_rootType(rootTypeFromCollectionType(type))
+    , m_invalidationType(invalidationTypeExcludingIdAndNameAttributes(type))
+    , m_shouldOnlyIncludeDirectChildren(shouldOnlyIncludeDirectChildren(type))
+    , m_isNameCacheValid(false)
+    , m_collectionType(type)
+    , m_overridesItemAfter(itemAfterOverrideType == OverridesItemAfter)
+    , m_isItemRefElementsCacheValid(false)
 {
+    ASSERT(m_rootType == static_cast<unsigned>(rootTypeFromCollectionType(type)));
+    ASSERT(m_invalidationType == static_cast<unsigned>(invalidationTypeExcludingIdAndNameAttributes(type)));
+    ASSERT(m_collectionType == static_cast<unsigned>(type));
+
+    document().registerCollection(this);
 }
 
 PassRefPtr<HTMLCollection> HTMLCollection::create(ContainerNode& base, CollectionType type)
@@ -167,15 +158,21 @@ PassRefPtr<HTMLCollection> HTMLCollection::create(ContainerNode& base, Collectio
 
 HTMLCollection::~HTMLCollection()
 {
+    document().unregisterCollection(this);
     // HTMLNameCollection removes cache by itself.
     if (type() != WindowNamedItems && type() != DocumentNamedItems)
-        ownerNode().nodeLists()->removeCacheWithAtomicName(this, type());
+        ownerNode().nodeLists()->removeCachedCollection(this);
 }
 
-template <class NodeListType>
-inline bool isMatchingElement(const NodeListType*, Element*);
+ContainerNode& HTMLCollection::rootNode() const
+{
+    if (isRootedAtDocument() && ownerNode().inDocument())
+        return ownerNode().document();
 
-template <> inline bool isMatchingElement(const HTMLCollection* htmlCollection, Element* element)
+    return ownerNode();
+}
+
+template inline bool isMatchingElement(const HTMLCollection* htmlCollection, Element* element)
 {
     CollectionType type = htmlCollection->type();
     if (!element->isHTMLElement() && !(type == DocAll || type == NodeChildren || type == WindowNamedItems))
@@ -224,32 +221,12 @@ template <> inline bool isMatchingElement(const HTMLCollection* htmlCollection,
         return static_cast<const WindowNameCollection*>(htmlCollection)->nodeMatches(element);
     case FormControls:
     case TableRows:
-    case ClassNodeListType:
-    case NameNodeListType:
-    case TagNodeListType:
-    case HTMLTagNodeListType:
-    case RadioNodeListType:
-    case LabelsNodeListType:
-        ASSERT_NOT_REACHED();
+        break;
     }
+    ASSERT_NOT_REACHED();
     return false;
 }
 
-template <> inline bool isMatchingElement(const LiveNodeList* nodeList, Element* element)
-{
-    return nodeList->nodeMatches(element);
-}
-
-template <> inline bool isMatchingElement(const HTMLTagNodeList* nodeList, Element* element)
-{
-    return nodeList->nodeMatchesInlined(element);
-}
-
-template <> inline bool isMatchingElement(const ClassNodeList* nodeList, Element* element)
-{
-    return nodeList->nodeMatchesInlined(element);
-}
-
 static Element* previousElement(ContainerNode& base, Element* previous, bool onlyIncludeDirectChildren)
 {
     return onlyIncludeDirectChildren ? ElementTraversal::previousSibling(previous) : ElementTraversal::previous(previous, &base);
@@ -260,86 +237,55 @@ static Element* lastElement(ContainerNode& rootNode, bool onlyIncludeDirectChild
     return onlyIncludeDirectChildren ? ElementTraversal::lastChild(&rootNode) : ElementTraversal::lastWithin(&rootNode);
 }
 
-ALWAYS_INLINE Element* LiveNodeListBase::iterateForPreviousElement(Element* current) const
+ALWAYS_INLINE Element* HTMLCollection::iterateForPreviousElement(Element* current) const
 {
-    bool onlyIncludeDirectChildren = shouldOnlyIncludeDirectChildren();
-    CollectionType collectionType = type();
+    bool onlyIncludeDirectChildren = m_shouldOnlyIncludeDirectChildren;
     ContainerNode& rootNode = this->rootNode();
     for (; current; current = previousElement(rootNode, current, onlyIncludeDirectChildren)) {
-        if (isNodeList(collectionType)) {
-            if (isMatchingElement(static_cast<const LiveNodeList*>(this), current))
-                return current;
-        } else {
-            if (isMatchingElement(static_cast<const HTMLCollection*>(this), current))
-                return current;
-        }
+        if (isMatchingElement(this, current))
+            return current;
     }
     return 0;
 }
 
-ALWAYS_INLINE Element* LiveNodeListBase::itemBefore(Element* previous) const
+ALWAYS_INLINE Element* HTMLCollection::itemBefore(Element* previous) const
 {
     Element* current;
     if (LIKELY(!!previous)) // Without this LIKELY, length() and item() can be 10% slower.
-        current = previousElement(rootNode(), previous, shouldOnlyIncludeDirectChildren());
+        current = previousElement(rootNode(), previous, m_shouldOnlyIncludeDirectChildren);
     else
-        current = lastElement(rootNode(), shouldOnlyIncludeDirectChildren());
+        current = lastElement(rootNode(), m_shouldOnlyIncludeDirectChildren);
 
     return iterateForPreviousElement(current);
 }
 
-template <class NodeListType>
-inline Element* firstMatchingElement(const NodeListType* nodeList, ContainerNode* root)
+inline Element* firstMatchingElement(const HTMLCollection* collection, ContainerNode* root)
 {
     Element* element = ElementTraversal::firstWithin(root);
-    while (element && !isMatchingElement(nodeList, element))
+    while (element && !isMatchingElement(collection, element))
         element = ElementTraversal::next(element, root);
     return element;
 }
 
-template <class NodeListType>
-inline Element* nextMatchingElement(const NodeListType* nodeList, Element* current, ContainerNode* root)
+inline Element* nextMatchingElement(const HTMLCollection* collection, Element* current, ContainerNode* root)
 {
     do {
         current = ElementTraversal::next(current, root);
-    } while (current && !isMatchingElement(nodeList, current));
+    } while (current && !isMatchingElement(collection, current));
     return current;
 }
 
-template <class NodeListType>
-inline Element* traverseMatchingElementsForwardToOffset(const NodeListType* nodeList, unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root)
+inline Element* traverseMatchingElementsForwardToOffset(const HTMLCollection* collection, unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root)
 {
     ASSERT_WITH_SECURITY_IMPLICATION(currentOffset < offset);
-    while ((currentElement = nextMatchingElement(nodeList, currentElement, root))) {
+    while ((currentElement = nextMatchingElement(collection, currentElement, root))) {
         if (++currentOffset == offset)
             return currentElement;
     }
     return 0;
 }
 
-// FIXME: This should be in LiveNodeList
-inline Element* LiveNodeListBase::traverseLiveNodeListFirstElement(ContainerNode* root) const
-{
-    ASSERT(isNodeList(type()));
-    if (type() == HTMLTagNodeListType)
-        return firstMatchingElement(static_cast<const HTMLTagNodeList*>(this), root);
-    if (type() == ClassNodeListType)
-        return firstMatchingElement(static_cast<const ClassNodeList*>(this), root);
-    return firstMatchingElement(static_cast<const LiveNodeList*>(this), root);
-}
-
-// FIXME: This should be in LiveNodeList
-inline Element* LiveNodeListBase::traverseLiveNodeListForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root) const
-{
-    ASSERT(isNodeList(type()));
-    if (type() == HTMLTagNodeListType)
-        return traverseMatchingElementsForwardToOffset(static_cast<const HTMLTagNodeList*>(this), offset, currentElement, currentOffset, root);
-    if (type() == ClassNodeListType)
-        return traverseMatchingElementsForwardToOffset(static_cast<const ClassNodeList*>(this), offset, currentElement, currentOffset, root);
-    return traverseMatchingElementsForwardToOffset(static_cast<const LiveNodeList*>(this), offset, currentElement, currentOffset, root);
-}
-
-bool ALWAYS_INLINE LiveNodeListBase::isLastItemCloserThanLastOrCachedItem(unsigned offset) const
+bool ALWAYS_INLINE HTMLCollection::isLastItemCloserThanLastOrCachedItem(unsigned offset) const
 {
     ASSERT(isLengthCacheValid());
     unsigned distanceFromLastItem = cachedLength() - offset;
@@ -349,7 +295,7 @@ bool ALWAYS_INLINE LiveNodeListBase::isLastItemCloserThanLastOrCachedItem(unsign
     return cachedElementOffset() < offset && distanceFromLastItem < offset - cachedElementOffset();
 }
     
-bool ALWAYS_INLINE LiveNodeListBase::isFirstItemCloserThanCachedItem(unsigned offset) const
+bool ALWAYS_INLINE HTMLCollection::isFirstItemCloserThanCachedItem(unsigned offset) const
 {
     ASSERT(isElementCacheValid());
     if (cachedElementOffset() < offset)
@@ -359,7 +305,7 @@ bool ALWAYS_INLINE LiveNodeListBase::isFirstItemCloserThanCachedItem(unsigned of
     return offset < distanceFromCachedItem;
 }
 
-ALWAYS_INLINE void LiveNodeListBase::setCachedElement(Element& element, unsigned offset, unsigned elementsArrayOffset) const
+ALWAYS_INLINE void HTMLCollection::setCachedElement(Element& element, unsigned offset, unsigned elementsArrayOffset) const
 {
     setCachedElement(element, offset);
     if (overridesItemAfter())
@@ -368,7 +314,7 @@ ALWAYS_INLINE void LiveNodeListBase::setCachedElement(Element& element, unsigned
     ASSERT(overridesItemAfter() || !elementsArrayOffset);
 }
 
-unsigned LiveNodeListBase::length() const
+unsigned HTMLCollection::length() const
 {
     if (isLengthCacheValid())
         return cachedLength();
@@ -379,8 +325,7 @@ unsigned LiveNodeListBase::length() const
     return cachedLength();
 }
 
-// FIXME: It is silly that these functions are in HTMLCollection.cpp.
-Node* LiveNodeListBase::item(unsigned offset) const
+Node* HTMLCollection::item(unsigned offset) const
 {
     if (isElementCacheValid() && cachedElementOffset() == offset)
         return cachedElement();
@@ -396,11 +341,7 @@ Node* LiveNodeListBase::item(unsigned offset) const
         setCachedElement(*lastItem, cachedLength() - 1, 0);
     } else if (!isElementCacheValid() || isFirstItemCloserThanCachedItem(offset) || (overridesItemAfter() && offset < cachedElementOffset())) {
         unsigned offsetInArray = 0;
-        Element* firstItem;
-        if (isNodeList(type()))
-            firstItem = traverseLiveNodeListFirstElement(&root);
-        else
-            firstItem = static_cast<const HTMLCollection*>(this)->traverseFirstElement(offsetInArray, &root);
+        Element* firstItem = traverseFirstElement(offsetInArray, &root);
 
         if (!firstItem) {
             setLengthCache(0);
@@ -416,7 +357,7 @@ Node* LiveNodeListBase::item(unsigned offset) const
     return elementBeforeOrAfterCachedElement(offset, &root);
 }
 
-inline Element* LiveNodeListBase::elementBeforeOrAfterCachedElement(unsigned offset, ContainerNode* root) const
+inline Element* HTMLCollection::elementBeforeOrAfterCachedElement(unsigned offset, ContainerNode* root) const
 {
     unsigned currentOffset = cachedElementOffset();
     Element* currentItem = cachedElement();
@@ -438,10 +379,7 @@ inline Element* LiveNodeListBase::elementBeforeOrAfterCachedElement(unsigned off
     }
 
     unsigned offsetInArray = 0;
-    if (isNodeList(type()))
-        currentItem = traverseLiveNodeListForwardToOffset(offset, currentItem, currentOffset, root);
-    else
-        currentItem = static_cast<const HTMLCollection*>(this)->traverseForwardToOffset(offset, currentItem, currentOffset, offsetInArray, root);
+    currentItem = traverseForwardToOffset(offset, currentItem, currentOffset, offsetInArray, root);
 
     if (!currentItem) {
         // Did not find the item. On plus side, we now know the length.
@@ -492,7 +430,7 @@ inline Element* HTMLCollection::traverseFirstElement(unsigned& offsetInArray, Co
     if (overridesItemAfter())
         return virtualItemAfter(offsetInArray, 0);
     ASSERT(!offsetInArray);
-    if (shouldOnlyIncludeDirectChildren())
+    if (m_shouldOnlyIncludeDirectChildren)
         return firstMatchingChildElement(static_cast<const HTMLCollection*>(this), root);
     return firstMatchingElement(static_cast<const HTMLCollection*>(this), root);
 }
@@ -502,7 +440,7 @@ inline Element* HTMLCollection::traverseNextElement(unsigned& offsetInArray, Ele
     if (overridesItemAfter())
         return virtualItemAfter(offsetInArray, previous);
     ASSERT(!offsetInArray);
-    if (shouldOnlyIncludeDirectChildren())
+    if (m_shouldOnlyIncludeDirectChildren)
         return nextMatchingSiblingElement(this, previous);
     return nextMatchingElement(this, previous, root);
 }
@@ -518,7 +456,7 @@ inline Element* HTMLCollection::traverseForwardToOffset(unsigned offset, Element
         }
         return 0;
     }
-    if (shouldOnlyIncludeDirectChildren()) {
+    if (m_shouldOnlyIncludeDirectChildren) {
         while ((currentElement = nextMatchingSiblingElement(this, currentElement))) {
             if (++currentOffset == offset)
                 return currentElement;
@@ -528,6 +466,24 @@ inline Element* HTMLCollection::traverseForwardToOffset(unsigned offset, Element
     return traverseMatchingElementsForwardToOffset(this, offset, currentElement, currentOffset, root);
 }
 
+void HTMLCollection::invalidateCache() const
+{
+    m_cachedElement = nullptr;
+    m_isLengthCacheValid = false;
+    m_isElementCacheValid = false;
+    m_isNameCacheValid = false;
+    m_isItemRefElementsCacheValid = false;
+    m_idCache.clear();
+    m_nameCache.clear();
+    m_cachedElementsArrayOffset = 0;
+}
+
+void HTMLCollection::invalidateIdNameCacheMaps() const
+{
+    m_idCache.clear();
+    m_nameCache.clear();
+}
+
 Node* HTMLCollection::namedItem(const AtomicString& name) const
 {
     // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp
@@ -557,7 +513,7 @@ Node* HTMLCollection::namedItem(const AtomicString& name) const
 
         if (candidate
             && isMatchingElement(this, candidate)
-            && (shouldOnlyIncludeDirectChildren() ? candidate->parentNode() == &root : candidate->isDescendantOf(&root)))
+            && (m_shouldOnlyIncludeDirectChildren ? candidate->parentNode() == &root : candidate->isDescendantOf(&root)))
             return candidate;
     }
 
index b317b40..c749b49 100644 (file)
@@ -24,6 +24,9 @@
 #define HTMLCollection_h
 
 #include "CollectionType.h"
+#include "ContainerNode.h"
+#include "Document.h"
+#include "HTMLNames.h"
 #include "LiveNodeList.h"
 #include "ScriptWrappable.h"
 #include <wtf/Forward.h>
 
 namespace WebCore {
 
-class HTMLCollection : public LiveNodeListBase {
+class Element;
+
+class HTMLCollection : public ScriptWrappable, public RefCounted<HTMLCollection> {
 public:
+    enum ItemAfterOverrideType {
+        OverridesItemAfter,
+        DoesNotOverrideItemAfter,
+    };
+
     static PassRefPtr<HTMLCollection> create(ContainerNode& base, CollectionType);
     virtual ~HTMLCollection();
 
     // DOM API
-    virtual Node* namedItem(const AtomicString& name) const OVERRIDE;
+    unsigned length() const;
+    Node* item(unsigned offset) const;
+    virtual Node* namedItem(const AtomicString& name) const;
     PassRefPtr<NodeList> tags(const String&);
 
     // Non-DOM API
@@ -66,6 +78,20 @@ public:
     Element* traverseFirstElement(unsigned& offsetInArray, ContainerNode* root) const;
     Element* traverseForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, unsigned& offsetInArray, ContainerNode* root) const;
 
+    ALWAYS_INLINE bool isRootedAtDocument() const { return m_rootType == NodeListIsRootedAtDocument; }
+    ALWAYS_INLINE NodeListInvalidationType invalidationType() const { return static_cast<NodeListInvalidationType>(m_invalidationType); }
+    ALWAYS_INLINE CollectionType type() const { return static_cast<CollectionType>(m_collectionType); }
+    ContainerNode& ownerNode() const { return const_cast<ContainerNode&>(m_ownerNode.get()); }
+    ALWAYS_INLINE void invalidateCache(const QualifiedName* attrName) const
+    {
+        if (!attrName || shouldInvalidateTypeOnAttributeChange(invalidationType(), *attrName))
+            invalidateCache();
+        else if (*attrName == HTMLNames::idAttr || *attrName == HTMLNames::nameAttr)
+            invalidateIdNameCacheMaps();
+    }
+    void invalidateCache() const;
+    void invalidateIdNameCacheMaps() const;
+
 protected:
     HTMLCollection(ContainerNode& base, CollectionType, ItemAfterOverrideType);
 
@@ -77,18 +103,67 @@ protected:
     void appendIdCache(const AtomicString& name, Element* element) const { append(m_idCache, name, element); }
     void appendNameCache(const AtomicString& name, Element* element) const { append(m_nameCache, name, element); }
 
+    Document& document() const { return m_ownerNode->document(); }
+    ContainerNode& rootNode() const;
+    bool overridesItemAfter() const { return m_overridesItemAfter; }
+
+    ALWAYS_INLINE bool isElementCacheValid() const { return m_isElementCacheValid; }
+    ALWAYS_INLINE Element* cachedElement() const { return m_cachedElement; }
+    ALWAYS_INLINE unsigned cachedElementOffset() const { return m_cachedElementOffset; }
+
+    ALWAYS_INLINE bool isLengthCacheValid() const { return m_isLengthCacheValid; }
+    ALWAYS_INLINE unsigned cachedLength() const { return m_cachedLength; }
+    ALWAYS_INLINE void setLengthCache(unsigned length) const
+    {
+        m_cachedLength = length;
+        m_isLengthCacheValid = true;
+    }
+    ALWAYS_INLINE void setCachedElement(Element& element, unsigned offset) const
+    {
+        m_cachedElement = &element;
+        m_cachedElementOffset = offset;
+        m_isElementCacheValid = true;
+    }
+    void setCachedElement(Element&, unsigned offset, unsigned elementsArrayOffset) const;
+
+    ALWAYS_INLINE bool isItemRefElementsCacheValid() const { return m_isItemRefElementsCacheValid; }
+    ALWAYS_INLINE void setItemRefElementsCacheValid() const { m_isItemRefElementsCacheValid = true; }
+
+    ALWAYS_INLINE NodeListRootType rootType() const { return static_cast<NodeListRootType>(m_rootType); }
+
+    bool hasNameCache() const { return m_isNameCacheValid; }
+    void setHasNameCache() const { m_isNameCacheValid = true; }
+
 private:
     Element* traverseNextElement(unsigned& offsetInArray, Element* previous, ContainerNode* root) const;
 
-    virtual bool isLiveNodeList() const OVERRIDE { ASSERT_NOT_REACHED(); return true; }
-
     static void append(NodeCacheMap&, const AtomicString&, Element*);
 
+    Element* elementBeforeOrAfterCachedElement(unsigned offset, ContainerNode* root) const;
+    bool isLastItemCloserThanLastOrCachedItem(unsigned offset) const;
+    bool isFirstItemCloserThanCachedItem(unsigned offset) const;
+    Element* iterateForPreviousElement(Element* current) const;
+    Element* itemBefore(Element* previousItem) const;
+
+    Ref<ContainerNode> m_ownerNode;
+    mutable Element* m_cachedElement;
+    mutable unsigned m_cachedLength;
+    mutable unsigned m_cachedElementOffset;
+    mutable unsigned m_isLengthCacheValid : 1;
+    mutable unsigned m_isElementCacheValid : 1;
+    const unsigned m_rootType : 2;
+    const unsigned m_invalidationType : 4;
+    const unsigned m_shouldOnlyIncludeDirectChildren : 1;
+
+    // From HTMLCollection
+    mutable unsigned m_isNameCacheValid : 1;
+    const unsigned m_collectionType : 5;
+    const unsigned m_overridesItemAfter : 1;
+    mutable unsigned m_isItemRefElementsCacheValid : 1;
+
     mutable NodeCacheMap m_idCache;
     mutable NodeCacheMap m_nameCache;
     mutable unsigned m_cachedElementsArrayOffset;
-
-    friend class LiveNodeListBase;
 };
 
 } // namespace
index 77e149a..ef09f0b 100644 (file)
@@ -46,7 +46,7 @@ HTMLNameCollection::~HTMLNameCollection()
 {
     ASSERT(type() == WindowNamedItems || type() == DocumentNamedItems);
 
-    document().nodeLists()->removeCacheWithAtomicName(this, type(), m_name);
+    document().nodeLists()->removeCachedCollection(this, m_name);
 }
 
 bool WindowNameCollection::nodeMatchesIfNameAttributeMatch(Element* element)
index ee5fde8..1d4d7f9 100644 (file)
@@ -45,7 +45,7 @@ PassRefPtr<NodeList> LabelableElement::labels()
     if (!supportLabels())
         return 0;
 
-    return ensureRareData().ensureNodeLists().addCacheWithAtomicName<LabelsNodeList>(*this, LabelsNodeListType, starAtom);
+    return ensureRareData().ensureNodeLists().addCacheWithAtomicName<LabelsNodeList>(*this, LiveNodeList::LabelsNodeListType, starAtom);
 }
 
 } // namespace Webcore
index e389338..8a9a348 100644 (file)
@@ -40,7 +40,7 @@ LabelsNodeList::LabelsNodeList(LabelableElement& forNode)
 
 LabelsNodeList::~LabelsNodeList()
 {
-    ownerNode().nodeLists()->removeCacheWithAtomicName(this, LabelsNodeListType, starAtom);
+    ownerNode().nodeLists()->removeCacheWithAtomicName(this, starAtom);
 } 
     
 bool LabelsNodeList::nodeMatches(Element* testNode) const
index 3f642f4..3040a52 100644 (file)
@@ -32,7 +32,7 @@ namespace WebCore {
 
 class LabelsNodeList FINAL : public LiveNodeList {
 public:
-    static PassRef<LabelsNodeList> create(LabelableElement& forNode, CollectionType type, const AtomicString&)
+    static PassRef<LabelsNodeList> create(LabelableElement& forNode, Type type, const AtomicString&)
     {
         ASSERT_UNUSED(type, type == LabelsNodeListType);
         return adoptRef(*new LabelsNodeList(forNode));
index d0f819a..d614169 100644 (file)
@@ -45,7 +45,7 @@ RadioNodeList::RadioNodeList(ContainerNode& rootNode, const AtomicString& name)
 
 RadioNodeList::~RadioNodeList()
 {
-    ownerNode().nodeLists()->removeCacheWithAtomicName(this, RadioNodeListType, m_name);
+    ownerNode().nodeLists()->removeCacheWithAtomicName(this, m_name);
 }
 
 static inline HTMLInputElement* toRadioButtonInputElement(Node* node)
index 36647c9..03d91fa 100644 (file)
@@ -34,7 +34,7 @@ namespace WebCore {
 
 class RadioNodeList : public LiveNodeList {
 public:
-    static PassRefPtr<RadioNodeList> create(ContainerNode& rootNode, CollectionType type, const AtomicString& name)
+    static PassRefPtr<RadioNodeList> create(ContainerNode& rootNode, Type type, const AtomicString& name)
     {
         ASSERT_UNUSED(type, type == RadioNodeListType);
         return adoptRef(new RadioNodeList(rootNode, name));