Title: [138195] trunk/Source/WebCore
Revision
138195
Author
[email protected]
Date
2012-12-19 14:39:28 -0800 (Wed, 19 Dec 2012)

Log Message

Use ElementTraversal in LiveNodeListBase
https://bugs.webkit.org/show_bug.cgi?id=105324

Reviewed by Ryosuke Niwa.

Factor the code so that we get clean minimally branchy traversal functions for all the common cases.

This patch changes the more performance critical forward traversal only, backwards traversal is unaffected for now.

Instruments thinks it is a progression at least in DOM/DOMDivWalk.html. Bots should tell more.

* dom/ClassNodeList.cpp:
(WebCore::ClassNodeList::nodeMatches):
* dom/ClassNodeList.h:
(ClassNodeList):
(WebCore::ClassNodeList::create):
(WebCore):
(WebCore::ClassNodeList::nodeMatchesInlined):

    Add inlined version of the matching function for class lists.

* dom/LiveNodeList.cpp:
(WebCore::LiveNodeListBase::rootContainerNode):

    Root is always ContainerNode if the list has anything in it. Traversal functions are slightly more
    efficient if we know we are operating on ContainerNodes.

(WebCore):
* dom/LiveNodeList.h:
(LiveNodeListBase):
(WebCore::LiveNodeListBase::shouldOnlyIncludeDirectChildren):
* dom/Node.cpp:
(WebCore::Node::getElementsByTagName):
* dom/TagNodeList.cpp:
(WebCore::TagNodeList::TagNodeList):
(WebCore::TagNodeList::~TagNodeList):
(WebCore::HTMLTagNodeList::HTMLTagNodeList):
(WebCore::HTMLTagNodeList::nodeMatches):
* dom/TagNodeList.h:
(WebCore):
(TagNodeList):
(WebCore::TagNodeList::create):
(HTMLTagNodeList):
(WebCore::HTMLTagNodeList::create):

    Use separate ContainerType enum value for HTMLTagNodeList so we can tell it apart from TagNodeList.

(WebCore::HTMLTagNodeList::nodeMatchesInlined):

    Add inlined version of the matching function for tag lists.

* html/CollectionType.h:
* html/HTMLCollection.cpp:
(WebCore::shouldOnlyIncludeDirectChildren):
(WebCore::rootTypeFromCollectionType):
(WebCore::invalidationTypeExcludingIdAndNameAttributes):
(WebCore):
(WebCore::isMatchingElement):
        
    List type templated matching functions for common cases.

(WebCore::HTMLCollection::HTMLCollection):
(WebCore::HTMLCollection::create):
(WebCore::HTMLCollection::~HTMLCollection):
(WebCore::previousNode):
(WebCore::lastNode):
(WebCore::LiveNodeListBase::iterateForPreviousNode):
(WebCore::LiveNodeListBase::itemBefore):

    Leave the backwards traversal unchanged for now but remove the forward traversal code.

(WebCore::firstMatchingElement):
(WebCore::nextMatchingElement):
(WebCore::firstMatchingChildElement):
(WebCore::nextMatchingChildElement):
(WebCore::traverseMatchingElementsForwardToOffset):

    List type templated traversal functions with matching. Separate functions for first and subsequent elements

(WebCore::LiveNodeListBase::traverseChildNodeListForwardToOffset):
(WebCore::LiveNodeListBase::traverseLiveNodeListFirstElement):
(WebCore::LiveNodeListBase::traverseLiveNodeListForwardToOffset):

    LiveNodeList traversal, picking the right template.

(WebCore::LiveNodeListBase::item):
(WebCore::LiveNodeListBase::itemBeforeOrAfterCachedItem):

    Switch to new traversal functions.

(WebCore::HTMLCollection::traverseFirstElement):
(WebCore::HTMLCollection::traverseForwardToOffset):
(WebCore::HTMLCollection::traverseNextElement):

    HTMLCollection traversal, picking the right template.

(WebCore::HTMLCollection::namedItem):
(WebCore::HTMLCollection::updateNameCache):

    Switch to new traversal functions.

* html/HTMLCollection.h:
(HTMLCollection):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (138194 => 138195)


--- trunk/Source/WebCore/ChangeLog	2012-12-19 21:59:25 UTC (rev 138194)
+++ trunk/Source/WebCore/ChangeLog	2012-12-19 22:39:28 UTC (rev 138195)
@@ -1,3 +1,109 @@
+2012-12-19  Antti Koivisto  <[email protected]>
+
+        Use ElementTraversal in LiveNodeListBase
+        https://bugs.webkit.org/show_bug.cgi?id=105324
+
+        Reviewed by Ryosuke Niwa.
+
+        Factor the code so that we get clean minimally branchy traversal functions for all the common cases.
+
+        This patch changes the more performance critical forward traversal only, backwards traversal is unaffected for now.
+
+        Instruments thinks it is a progression at least in DOM/DOMDivWalk.html. Bots should tell more.
+
+        * dom/ClassNodeList.cpp:
+        (WebCore::ClassNodeList::nodeMatches):
+        * dom/ClassNodeList.h:
+        (ClassNodeList):
+        (WebCore::ClassNodeList::create):
+        (WebCore):
+        (WebCore::ClassNodeList::nodeMatchesInlined):
+
+            Add inlined version of the matching function for class lists.
+
+        * dom/LiveNodeList.cpp:
+        (WebCore::LiveNodeListBase::rootContainerNode):
+
+            Root is always ContainerNode if the list has anything in it. Traversal functions are slightly more
+            efficient if we know we are operating on ContainerNodes.
+
+        (WebCore):
+        * dom/LiveNodeList.h:
+        (LiveNodeListBase):
+        (WebCore::LiveNodeListBase::shouldOnlyIncludeDirectChildren):
+        * dom/Node.cpp:
+        (WebCore::Node::getElementsByTagName):
+        * dom/TagNodeList.cpp:
+        (WebCore::TagNodeList::TagNodeList):
+        (WebCore::TagNodeList::~TagNodeList):
+        (WebCore::HTMLTagNodeList::HTMLTagNodeList):
+        (WebCore::HTMLTagNodeList::nodeMatches):
+        * dom/TagNodeList.h:
+        (WebCore):
+        (TagNodeList):
+        (WebCore::TagNodeList::create):
+        (HTMLTagNodeList):
+        (WebCore::HTMLTagNodeList::create):
+
+            Use separate ContainerType enum value for HTMLTagNodeList so we can tell it apart from TagNodeList.
+
+        (WebCore::HTMLTagNodeList::nodeMatchesInlined):
+
+            Add inlined version of the matching function for tag lists.
+
+        * html/CollectionType.h:
+        * html/HTMLCollection.cpp:
+        (WebCore::shouldOnlyIncludeDirectChildren):
+        (WebCore::rootTypeFromCollectionType):
+        (WebCore::invalidationTypeExcludingIdAndNameAttributes):
+        (WebCore):
+        (WebCore::isMatchingElement):
+        
+            List type templated matching functions for common cases.
+
+        (WebCore::HTMLCollection::HTMLCollection):
+        (WebCore::HTMLCollection::create):
+        (WebCore::HTMLCollection::~HTMLCollection):
+        (WebCore::previousNode):
+        (WebCore::lastNode):
+        (WebCore::LiveNodeListBase::iterateForPreviousNode):
+        (WebCore::LiveNodeListBase::itemBefore):
+
+            Leave the backwards traversal unchanged for now but remove the forward traversal code.
+
+        (WebCore::firstMatchingElement):
+        (WebCore::nextMatchingElement):
+        (WebCore::firstMatchingChildElement):
+        (WebCore::nextMatchingChildElement):
+        (WebCore::traverseMatchingElementsForwardToOffset):
+
+            List type templated traversal functions with matching. Separate functions for first and subsequent elements
+
+        (WebCore::LiveNodeListBase::traverseChildNodeListForwardToOffset):
+        (WebCore::LiveNodeListBase::traverseLiveNodeListFirstElement):
+        (WebCore::LiveNodeListBase::traverseLiveNodeListForwardToOffset):
+
+            LiveNodeList traversal, picking the right template.
+
+        (WebCore::LiveNodeListBase::item):
+        (WebCore::LiveNodeListBase::itemBeforeOrAfterCachedItem):
+
+            Switch to new traversal functions.
+
+        (WebCore::HTMLCollection::traverseFirstElement):
+        (WebCore::HTMLCollection::traverseForwardToOffset):
+        (WebCore::HTMLCollection::traverseNextElement):
+
+            HTMLCollection traversal, picking the right template.
+
+        (WebCore::HTMLCollection::namedItem):
+        (WebCore::HTMLCollection::updateNameCache):
+
+            Switch to new traversal functions.
+
+        * html/HTMLCollection.h:
+        (HTMLCollection):
+
 2012-12-19  Alexey Proskuryakov  <[email protected]>
 
         <rdar://problem/12896478> Cannot log into gmail/facebook with NetworkProcess and private browsing enabled

Modified: trunk/Source/WebCore/dom/ClassNodeList.cpp (138194 => 138195)


--- trunk/Source/WebCore/dom/ClassNodeList.cpp	2012-12-19 21:59:25 UTC (rev 138194)
+++ trunk/Source/WebCore/dom/ClassNodeList.cpp	2012-12-19 22:39:28 UTC (rev 138195)
@@ -50,15 +50,7 @@
 
 bool ClassNodeList::nodeMatches(Element* testNode) const
 {
-    if (!testNode->hasClass())
-        return false;
-    if (!m_classNames.size())
-        return false;
-    // FIXME: DOM4 allows getElementsByClassName to return non StyledElement.
-    // https://bugs.webkit.org/show_bug.cgi?id=94718
-    if (!testNode->isStyledElement())
-        return false;
-    return testNode->classNames().containsAll(m_classNames);
+    return nodeMatchesInlined(testNode);
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/dom/ClassNodeList.h (138194 => 138195)


--- trunk/Source/WebCore/dom/ClassNodeList.h	2012-12-19 21:59:25 UTC (rev 138194)
+++ trunk/Source/WebCore/dom/ClassNodeList.h	2012-12-19 22:39:28 UTC (rev 138195)
@@ -30,30 +30,46 @@
 #ifndef ClassNodeList_h
 #define ClassNodeList_h
 
+#include "Element.h"
 #include "LiveNodeList.h"
 #include "Node.h"
 #include "SpaceSplitString.h"
 
 namespace WebCore {
 
-    class ClassNodeList : public LiveNodeList {
-    public:
-        static PassRefPtr<ClassNodeList> create(PassRefPtr<Node> rootNode, const String& classNames)
-        {
-            return adoptRef(new ClassNodeList(rootNode, classNames));
-        }
+class ClassNodeList : public LiveNodeList {
+public:
+    static PassRefPtr<ClassNodeList> create(PassRefPtr<Node> rootNode, const String& classNames)
+    {
+        return adoptRef(new ClassNodeList(rootNode, classNames));
+    }
 
-        virtual ~ClassNodeList();
+    virtual ~ClassNodeList();
 
-    private:
-        ClassNodeList(PassRefPtr<Node> rootNode, const String& classNames);
+    bool nodeMatchesInlined(Element*) const;
 
-        virtual bool nodeMatches(Element*) const;
+private:
+    ClassNodeList(PassRefPtr<Node> rootNode, const String& classNames);
 
-        SpaceSplitString m_classNames;
-        String m_originalClassNames;
-    };
+    virtual bool nodeMatches(Element*) const;
 
+    SpaceSplitString m_classNames;
+    String m_originalClassNames;
+};
+
+inline bool ClassNodeList::nodeMatchesInlined(Element* testNode) const
+{
+    if (!testNode->hasClass())
+        return false;
+    if (!m_classNames.size())
+        return false;
+    // FIXME: DOM4 allows getElementsByClassName to return non StyledElement.
+    // https://bugs.webkit.org/show_bug.cgi?id=94718
+    if (!testNode->isStyledElement())
+        return false;
+    return testNode->classNames().containsAll(m_classNames);
+}
+
 } // namespace WebCore
 
 #endif // ClassNodeList_h

Modified: trunk/Source/WebCore/dom/LiveNodeList.cpp (138194 => 138195)


--- trunk/Source/WebCore/dom/LiveNodeList.cpp	2012-12-19 21:59:25 UTC (rev 138194)
+++ trunk/Source/WebCore/dom/LiveNodeList.cpp	2012-12-19 22:39:28 UTC (rev 138195)
@@ -52,6 +52,14 @@
     return m_ownerNode.get();
 }
 
+ContainerNode* LiveNodeListBase::rootContainerNode() const
+{
+    Node* rootNode = this->rootNode();
+    if (!rootNode->isContainerNode())
+        return 0;
+    return toContainerNode(rootNode);
+}
+
 void LiveNodeListBase::invalidateCache() const
 {
     m_cachedItem = 0;

Modified: trunk/Source/WebCore/dom/LiveNodeList.h (138194 => 138195)


--- trunk/Source/WebCore/dom/LiveNodeList.h	2012-12-19 21:59:25 UTC (rev 138194)
+++ trunk/Source/WebCore/dom/LiveNodeList.h	2012-12-19 22:39:28 UTC (rev 138195)
@@ -103,6 +103,7 @@
 protected:
     Document* document() const { return m_ownerNode->document(); }
     Node* rootNode() const;
+    ContainerNode* rootContainerNode() const;
     bool overridesItemAfter() const { return m_overridesItemAfter; }
 
     ALWAYS_INLINE bool isItemCacheValid() const { return m_isItemCacheValid; }
@@ -133,15 +134,16 @@
     bool hasNameCache() const { return m_isNameCacheValid; }
     void setHasNameCache() const { m_isNameCacheValid = true; }
 
-    Node* itemBeforeOrAfterCachedItem(unsigned offset) const;
-    Node* itemAfter(unsigned&, Node* previousItem) const;
+    bool shouldOnlyIncludeDirectChildren() const { return m_shouldOnlyIncludeDirectChildren; }
 
 private:
-    bool shouldOnlyIncludeDirectChildren() const { return m_shouldOnlyIncludeDirectChildren; }
+    Node* itemBeforeOrAfterCachedItem(unsigned offset, ContainerNode* root) const;
+    Node* traverseChildNodeListForwardToOffset(unsigned offset, Node* currentNode, unsigned& currentOffset) const;
+    Element* traverseLiveNodeListFirstElement(ContainerNode* root) const;
+    Element* traverseLiveNodeListForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root) const;
     bool isLastItemCloserThanLastOrCachedItem(unsigned offset) const;
     bool isFirstItemCloserThanCachedItem(unsigned offset) const;
-    template <bool forward> Node* iterateForNextNode(Node* current) const;
-    template<bool forward> Node* itemBeforeOrAfter(Node* previousItem) const;    
+    Node* iterateForPreviousNode(Node* current) const;
     Node* itemBefore(Node* previousItem) const;
 
     RefPtr<Node> m_ownerNode;

Modified: trunk/Source/WebCore/dom/Node.cpp (138194 => 138195)


--- trunk/Source/WebCore/dom/Node.cpp	2012-12-19 21:59:25 UTC (rev 138194)
+++ trunk/Source/WebCore/dom/Node.cpp	2012-12-19 22:39:28 UTC (rev 138195)
@@ -1355,7 +1355,7 @@
         return 0;
 
     if (document()->isHTMLDocument())
-        return ensureRareData()->ensureNodeLists()->addCacheWithAtomicName<HTMLTagNodeList>(this, TagNodeListType, localName);
+        return ensureRareData()->ensureNodeLists()->addCacheWithAtomicName<HTMLTagNodeList>(this, HTMLTagNodeListType, localName);
     return ensureRareData()->ensureNodeLists()->addCacheWithAtomicName<TagNodeList>(this, TagNodeListType, localName);
 }
 

Modified: trunk/Source/WebCore/dom/TagNodeList.cpp (138194 => 138195)


--- trunk/Source/WebCore/dom/TagNodeList.cpp	2012-12-19 21:59:25 UTC (rev 138194)
+++ trunk/Source/WebCore/dom/TagNodeList.cpp	2012-12-19 22:39:28 UTC (rev 138195)
@@ -30,8 +30,8 @@
 
 namespace WebCore {
 
-TagNodeList::TagNodeList(PassRefPtr<Node> rootNode, const AtomicString& namespaceURI, const AtomicString& localName)
-    : LiveNodeList(rootNode, TagNodeListType, DoNotInvalidateOnAttributeChanges)
+TagNodeList::TagNodeList(PassRefPtr<Node> rootNode, CollectionType type, const AtomicString& namespaceURI, const AtomicString& localName)
+    : LiveNodeList(rootNode, type, DoNotInvalidateOnAttributeChanges)
     , m_namespaceURI(namespaceURI)
     , m_localName(localName)
 {
@@ -41,7 +41,7 @@
 TagNodeList::~TagNodeList()
 {
     if (m_namespaceURI == starAtom)
-        ownerNode()->nodeLists()->removeCacheWithAtomicName(this, TagNodeListType, m_localName);
+        ownerNode()->nodeLists()->removeCacheWithAtomicName(this, type(), m_localName);
     else
         ownerNode()->nodeLists()->removeCacheWithQualifiedName(this, m_namespaceURI, m_localName);
 }
@@ -56,22 +56,14 @@
 }
 
 HTMLTagNodeList::HTMLTagNodeList(PassRefPtr<Node> rootNode, const AtomicString& localName)
-    : TagNodeList(rootNode, starAtom, localName)
+    : TagNodeList(rootNode, HTMLTagNodeListType, starAtom, localName)
     , m_loweredLocalName(localName.lower())
 {
 }
 
 bool HTMLTagNodeList::nodeMatches(Element* testNode) const
 {
-    // Implements http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-getelementsbytagname
-    if (m_localName != starAtom) {
-        const AtomicString& localName = testNode->isHTMLElement() ? m_loweredLocalName : m_localName;
-        if (localName != testNode->localName())
-            return false;
-    }
-
-    ASSERT(m_namespaceURI == starAtom);
-    return true;
+    return nodeMatchesInlined(testNode);
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/dom/TagNodeList.h (138194 => 138195)


--- trunk/Source/WebCore/dom/TagNodeList.h	2012-12-19 21:59:25 UTC (rev 138194)
+++ trunk/Source/WebCore/dom/TagNodeList.h	2012-12-19 22:39:28 UTC (rev 138195)
@@ -24,52 +24,68 @@
 #ifndef TagNodeList_h
 #define TagNodeList_h
 
+#include "Element.h"
 #include "LiveNodeList.h"
 #include <wtf/text/AtomicString.h>
 
 namespace WebCore {
 
-    // NodeList that limits to a particular tag.
-    class TagNodeList : public LiveNodeList {
-    public:
-        static PassRefPtr<TagNodeList> create(PassRefPtr<Node> rootNode, const AtomicString& namespaceURI, const AtomicString& localName)
-        {
-            ASSERT(namespaceURI != starAtom);
-            return adoptRef(new TagNodeList(rootNode, namespaceURI, localName));
-        }
+// NodeList that limits to a particular tag.
+class TagNodeList : public LiveNodeList {
+public:
+    static PassRefPtr<TagNodeList> create(PassRefPtr<Node> rootNode, const AtomicString& namespaceURI, const AtomicString& localName)
+    {
+        ASSERT(namespaceURI != starAtom);
+        return adoptRef(new TagNodeList(rootNode, TagNodeListType, namespaceURI, localName));
+    }
 
-        static PassRefPtr<TagNodeList> create(PassRefPtr<Node> rootNode, CollectionType type, const AtomicString& localName)
-        {
-            ASSERT_UNUSED(type, type == TagNodeListType);
-            return adoptRef(new TagNodeList(rootNode, starAtom, localName));
-        }
+    static PassRefPtr<TagNodeList> create(PassRefPtr<Node> rootNode, CollectionType type, const AtomicString& localName)
+    {
+        ASSERT_UNUSED(type, type == TagNodeListType);
+        return adoptRef(new TagNodeList(rootNode, TagNodeListType, starAtom, localName));
+    }
 
-        virtual ~TagNodeList();
+    virtual ~TagNodeList();
 
-    protected:
-        TagNodeList(PassRefPtr<Node> rootNode, const AtomicString& namespaceURI, const AtomicString& localName);
+protected:
+    TagNodeList(PassRefPtr<Node> rootNode, CollectionType, const AtomicString& namespaceURI, const AtomicString& localName);
 
-        virtual bool nodeMatches(Element*) const;
+    virtual bool nodeMatches(Element*) const;
 
-        AtomicString m_namespaceURI;
-        AtomicString m_localName;
-    };
+    AtomicString m_namespaceURI;
+    AtomicString m_localName;
+};
 
-    class HTMLTagNodeList : public TagNodeList {
-    public:
-        static PassRefPtr<HTMLTagNodeList> create(PassRefPtr<Node> rootNode, CollectionType type, const AtomicString& localName)
-        {
-            ASSERT_UNUSED(type, type == TagNodeListType);
-            return adoptRef(new HTMLTagNodeList(rootNode, localName));
-        }
+class HTMLTagNodeList : public TagNodeList {
+public:
+    static PassRefPtr<HTMLTagNodeList> create(PassRefPtr<Node> rootNode, CollectionType type, const AtomicString& localName)
+    {
+        ASSERT_UNUSED(type, type == HTMLTagNodeListType);
+        return adoptRef(new HTMLTagNodeList(rootNode, localName));
+    }
 
-    private:
-        HTMLTagNodeList(PassRefPtr<Node> rootNode, const AtomicString& localName);
+    bool nodeMatchesInlined(Element*) const;
 
-        virtual bool nodeMatches(Element*) const;
+private:
+    HTMLTagNodeList(PassRefPtr<Node> rootNode, const AtomicString& localName);
 
-        AtomicString m_loweredLocalName;
-    };
+    virtual bool nodeMatches(Element*) const;
+
+    AtomicString m_loweredLocalName;
+};
+
+inline bool HTMLTagNodeList::nodeMatchesInlined(Element* testNode) const
+{
+    // Implements http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-getelementsbytagname
+    if (m_localName != starAtom) {
+        const AtomicString& localName = testNode->isHTMLElement() ? m_loweredLocalName : m_localName;
+        if (localName != testNode->localName())
+            return false;
+    }
+    ASSERT(m_namespaceURI == starAtom);
+    return true;
+}
+
 } // namespace WebCore
 
 #endif // TagNodeList_h

Modified: trunk/Source/WebCore/html/CollectionType.h (138194 => 138195)


--- trunk/Source/WebCore/html/CollectionType.h	2012-12-19 21:59:25 UTC (rev 138194)
+++ trunk/Source/WebCore/html/CollectionType.h	2012-12-19 22:39:28 UTC (rev 138195)
@@ -60,6 +60,7 @@
     ClassNodeListType,
     NameNodeListType,
     TagNodeListType,
+    HTMLTagNodeListType,
     RadioNodeListType,
     LabelsNodeListType,
     MicroDataItemListType,

Modified: trunk/Source/WebCore/html/HTMLCollection.cpp (138194 => 138195)


--- trunk/Source/WebCore/html/HTMLCollection.cpp	2012-12-19 21:59:25 UTC (rev 138194)
+++ trunk/Source/WebCore/html/HTMLCollection.cpp	2012-12-19 22:39:28 UTC (rev 138195)
@@ -23,6 +23,7 @@
 #include "config.h"
 #include "HTMLCollection.h"
 
+#include "ClassNodeList.h"
 #include "HTMLDocument.h"
 #include "HTMLElement.h"
 #include "HTMLNames.h"
@@ -75,6 +76,7 @@
     case ClassNodeListType:
     case NameNodeListType:
     case TagNodeListType:
+    case HTMLTagNodeListType:
     case RadioNodeListType:
     case LabelsNodeListType:
     case MicroDataItemListType:
@@ -117,6 +119,7 @@
     case ClassNodeListType:
     case NameNodeListType:
     case TagNodeListType:
+    case HTMLTagNodeListType:
     case RadioNodeListType:
     case LabelsNodeListType:
     case MicroDataItemListType:
@@ -166,6 +169,7 @@
     case ClassNodeListType:
     case NameNodeListType:
     case TagNodeListType:
+    case HTMLTagNodeListType:
     case RadioNodeListType:
     case LabelsNodeListType:
     case MicroDataItemListType:
@@ -175,7 +179,6 @@
     ASSERT_NOT_REACHED();
     return DoNotInvalidateOnAttributeChanges;
 }
-    
 
 HTMLCollection::HTMLCollection(Node* ownerNode, CollectionType type, ItemAfterOverrideType itemAfterOverrideType)
     : LiveNodeListBase(ownerNode, rootTypeFromCollectionType(type), invalidationTypeExcludingIdAndNameAttributes(type),
@@ -195,8 +198,12 @@
         ownerNode()->nodeLists()->removeCacheWithAtomicName(this, type());
 }
 
-static inline bool isAcceptableElement(CollectionType type, Element* element)
+template <class NodeListType>
+inline bool isMatchingElement(const NodeListType*, Element*);
+
+template <> inline bool isMatchingElement(const HTMLCollection* htmlCollection, Element* element)
 {
+    CollectionType type = htmlCollection->type();
     if (!element->isHTMLElement() && !(type == DocAll || type == NodeChildren))
         return false;
 
@@ -249,6 +256,7 @@
     case ClassNodeListType:
     case NameNodeListType:
     case TagNodeListType:
+    case HTMLTagNodeListType:
     case RadioNodeListType:
     case LabelsNodeListType:
     case MicroDataItemListType:
@@ -258,15 +266,26 @@
     return false;
 }
 
-template<bool forward>
-static Node* nextNode(Node* base, Node* previous, bool onlyIncludeDirectChildren)
+template <> inline bool isMatchingElement(const LiveNodeList* nodeList, Element* element)
 {
-    if (forward)
-        return onlyIncludeDirectChildren ? previous->nextSibling() : NodeTraversal::next(previous, base);
-    else
-        return onlyIncludeDirectChildren ? previous->previousSibling() : NodeTraversal::previous(previous, base);
+    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 Node* previousNode(Node* base, Node* previous, bool onlyIncludeDirectChildren)
+{
+    return onlyIncludeDirectChildren ? previous->previousSibling() : NodeTraversal::previous(previous, base);
+}
+
 static inline Node* lastDescendent(Node* node)
 {
     node = node->lastChild();
@@ -275,64 +294,106 @@
     return node;
 }
 
-static Node* firstNode(bool forward, Node* rootNode, bool onlyIncludeDirectChildren)
+static Node* lastNode(Node* rootNode, bool onlyIncludeDirectChildren)
 {
-    if (forward)
-        return rootNode->firstChild();
-    else
-        return onlyIncludeDirectChildren ? rootNode->lastChild() : lastDescendent(rootNode);
+    return onlyIncludeDirectChildren ? rootNode->lastChild() : lastDescendent(rootNode);
 }
 
-template <bool forward>
-Node* LiveNodeListBase::iterateForNextNode(Node* current) const
+ALWAYS_INLINE Node* LiveNodeListBase::iterateForPreviousNode(Node* current) const
 {
     bool _onlyIncludeDirectChildren_ = shouldOnlyIncludeDirectChildren();
     CollectionType collectionType = type();
     Node* rootNode = this->rootNode();
-    for (; current; current = nextNode<forward>(rootNode, current, onlyIncludeDirectChildren)) {
+    for (; current; current = previousNode(rootNode, current, onlyIncludeDirectChildren)) {
         if (isNodeList(collectionType)) {
-            if (current->isElementNode() && static_cast<const LiveNodeList*>(this)->nodeMatches(toElement(current)))
+            if (current->isElementNode() && isMatchingElement(static_cast<const LiveNodeList*>(this), toElement(current)))
                 return toElement(current);
         } else {
-            if (current->isElementNode() && isAcceptableElement(collectionType, toElement(current)))
+            if (current->isElementNode() && isMatchingElement(static_cast<const HTMLCollection*>(this), toElement(current)))
                 return toElement(current);
         }
     }
-
     return 0;
 }
 
-// Without this ALWAYS_INLINE, length() and item() can be 100% slower.
-template<bool forward> ALWAYS_INLINE
-Node* LiveNodeListBase::itemBeforeOrAfter(Node* previous) const
+ALWAYS_INLINE Node* LiveNodeListBase::itemBefore(Node* previous) const
 {
     Node* current;
     if (LIKELY(!!previous)) // Without this LIKELY, length() and item() can be 10% slower.
-        current = nextNode<forward>(rootNode(), previous, shouldOnlyIncludeDirectChildren());
+        current = previousNode(rootNode(), previous, shouldOnlyIncludeDirectChildren());
     else
-        current = firstNode(forward, rootNode(), shouldOnlyIncludeDirectChildren());
+        current = lastNode(rootNode(), shouldOnlyIncludeDirectChildren());
 
-    if (isNodeList(type()) && shouldOnlyIncludeDirectChildren()) // ChildNodeList
+    if (type() == ChildNodeListType)
         return current;
+    return iterateForPreviousNode(current);
+}
 
-    return iterateForNextNode<forward>(current);
+template <class NodeListType>
+inline Element* firstMatchingElement(const NodeListType* nodeList, ContainerNode* root)
+{
+    Element* element = ElementTraversal::firstWithin(root);
+    while (element && !isMatchingElement(nodeList, element))
+        element = ElementTraversal::next(element, root);
+    return element;
 }
 
-// Without this ALWAYS_INLINE, length() and item() can be 100% slower.
-ALWAYS_INLINE Node* LiveNodeListBase::itemBefore(Node* previous) const
+template <class NodeListType>
+inline Element* nextMatchingElement(const NodeListType* nodeList, Element* current, ContainerNode* root)
 {
-    return itemBeforeOrAfter<false>(previous);
+    do {
+        current = ElementTraversal::next(current, root);
+    } while (current && !isMatchingElement(nodeList, current));
+    return current;
 }
 
-// Without this ALWAYS_INLINE, length() and item() can be 100% slower.
-ALWAYS_INLINE Node* LiveNodeListBase::itemAfter(unsigned& offsetInArray, Node* previous) const
+template <class NodeListType>
+inline Element* traverseMatchingElementsForwardToOffset(const NodeListType* nodeList, unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root)
 {
-    if (UNLIKELY(overridesItemAfter())) // Without this UNLIKELY, length() can be 100% slower.
-        return static_cast<const HTMLCollection*>(this)->virtualItemAfter(offsetInArray, toElement(previous));
-    ASSERT(!offsetInArray);
-    return itemBeforeOrAfter<true>(previous);
+    ASSERT(currentOffset < offset);
+    while ((currentElement = nextMatchingElement(nodeList, currentElement, root))) {
+        if (++currentOffset == offset)
+            return currentElement;
+    }
+    return 0;
 }
 
+// FIXME: This should be in ChildNodeList
+inline Node* LiveNodeListBase::traverseChildNodeListForwardToOffset(unsigned offset, Node* currentNode, unsigned& currentOffset) const
+{
+    ASSERT(type() == ChildNodeListType);
+    ASSERT(currentOffset < offset);
+    while ((currentNode = currentNode->nextSibling())) {
+        if (++currentOffset == offset)
+            return currentNode;
+    }
+    return 0;
+}
+
+// FIXME: This should be in LiveNodeList
+inline Element* LiveNodeListBase::traverseLiveNodeListFirstElement(ContainerNode* root) const
+{
+    ASSERT(isNodeList(type()));
+    ASSERT(type() != ChildNodeListType);
+    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()));
+    ASSERT(type() != ChildNodeListType);
+    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
 {
     ASSERT(isLengthCacheValid());
@@ -374,6 +435,7 @@
     return cachedLength();
 }
 
+// FIXME: It is silly that these functions are in HTMLCollection.cpp.
 Node* LiveNodeListBase::item(unsigned offset) const
 {
     if (isItemCacheValid() && cachedItemOffset() == offset)
@@ -389,13 +451,27 @@
         static_cast<const PropertyNodeList*>(this)->updateRefElements();
 #endif
 
+    ContainerNode* root = rootContainerNode();
+    if (!root) {
+        // FIMXE: In someTextNode.childNodes case the root is Text. We shouldn't even make a LiveNodeList for that.
+        setLengthCache(0);
+        return 0;
+    }
+
     if (isLengthCacheValid() && !overridesItemAfter() && isLastItemCloserThanLastOrCachedItem(offset)) {
         Node* lastItem = itemBefore(0);
         ASSERT(lastItem);
         setItemCache(lastItem, cachedLength() - 1, 0);
     } else if (!isItemCacheValid() || isFirstItemCloserThanCachedItem(offset) || (overridesItemAfter() && offset < cachedItemOffset())) {
         unsigned offsetInArray = 0;
-        Node* firstItem = itemAfter(offsetInArray, 0);
+        Node* firstItem;
+        if (type() == ChildNodeListType)
+            firstItem = root->firstChild();
+        else if (isNodeList(type()))
+            firstItem = traverseLiveNodeListFirstElement(root);
+        else
+            firstItem = static_cast<const HTMLCollection*>(this)->traverseFirstElement(offsetInArray, root);
+
         if (!firstItem) {
             setLengthCache(0);
             return 0;
@@ -407,13 +483,14 @@
     if (cachedItemOffset() == offset)
         return cachedItem();
 
-    return itemBeforeOrAfterCachedItem(offset);
+    return itemBeforeOrAfterCachedItem(offset, root);
 }
 
-Node* LiveNodeListBase::itemBeforeOrAfterCachedItem(unsigned offset) const
+inline Node* LiveNodeListBase::itemBeforeOrAfterCachedItem(unsigned offset, ContainerNode* root) const
 {
     unsigned currentOffset = cachedItemOffset();
     Node* currentItem = cachedItem();
+    ASSERT(currentItem);
     ASSERT(currentOffset != offset);
 
     if (offset < cachedItemOffset()) {
@@ -430,19 +507,21 @@
         return 0;
     }
 
-    unsigned offsetInArray = overridesItemAfter() ? static_cast<const HTMLCollection*>(this)->m_cachedElementsArrayOffset : 0;
-    while ((currentItem = itemAfter(offsetInArray, currentItem))) {
-        currentOffset++;
-        if (currentOffset == offset) {
-            setItemCache(currentItem, currentOffset, offsetInArray);
-            return currentItem;
-        }
+    unsigned offsetInArray = 0;
+    if (type() == ChildNodeListType)
+        currentItem = traverseChildNodeListForwardToOffset(offset, currentItem, currentOffset);
+    else if (isNodeList(type()))
+        currentItem = traverseLiveNodeListForwardToOffset(offset, toElement(currentItem), currentOffset, root);
+    else
+        currentItem = static_cast<const HTMLCollection*>(this)->traverseForwardToOffset(offset, toElement(currentItem), currentOffset, offsetInArray, root);
+
+    if (!currentItem) {
+        // Did not find the item. On plus side, we now know the length.
+        setLengthCache(currentOffset + 1);
+        return 0;
     }
-
-    unsigned offsetOfLastItem = currentOffset;
-    setLengthCache(offsetOfLastItem + 1);
-
-    return 0;
+    setItemCache(currentItem, currentOffset, offsetInArray);
+    return currentItem;
 }
 
 Element* HTMLCollection::virtualItemAfter(unsigned&, Element*) const
@@ -479,6 +558,63 @@
     return e->getNameAttribute() == name && e->getIdAttribute() != name;
 }
 
+inline Element* firstMatchingChildElement(const HTMLCollection* nodeList, ContainerNode* root)
+{
+    Element* element = ElementTraversal::firstWithin(root);
+    while (element && !isMatchingElement(nodeList, element))
+        element = ElementTraversal::nextSkippingChildren(element, root);
+    return element;
+}
+
+inline Element* nextMatchingChildElement(const HTMLCollection* nodeList, Element* current, ContainerNode* root)
+{
+    do {
+        current = ElementTraversal::nextSkippingChildren(current, root);
+    } while (current && !isMatchingElement(nodeList, current));
+    return current;
+}
+
+inline Element* HTMLCollection::traverseFirstElement(unsigned& offsetInArray, ContainerNode* root) const
+{
+    if (overridesItemAfter())
+        return virtualItemAfter(offsetInArray, 0);
+    ASSERT(!offsetInArray);
+    if (shouldOnlyIncludeDirectChildren())
+        return firstMatchingChildElement(static_cast<const HTMLCollection*>(this), root);
+    return firstMatchingElement(static_cast<const HTMLCollection*>(this), root);
+}
+
+inline Element* HTMLCollection::traverseNextElement(unsigned& offsetInArray, Element* previous, ContainerNode* root) const
+{
+    if (overridesItemAfter())
+        return virtualItemAfter(offsetInArray, previous);
+    ASSERT(!offsetInArray);
+    if (shouldOnlyIncludeDirectChildren())
+        return nextMatchingChildElement(this, previous, root);
+    return nextMatchingElement(this, previous, root);
+}
+
+inline Element* HTMLCollection::traverseForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, unsigned& offsetInArray, ContainerNode* root) const
+{
+    ASSERT(currentOffset < offset);
+    if (overridesItemAfter()) {
+        offsetInArray = m_cachedElementsArrayOffset;
+        while ((currentElement = virtualItemAfter(offsetInArray, currentElement))) {
+            if (++currentOffset == offset)
+                return currentElement;
+        }
+        return 0;
+    }
+    if (shouldOnlyIncludeDirectChildren()) {
+        while ((currentElement = nextMatchingChildElement(this, currentElement, root))) {
+            if (++currentOffset == offset)
+                return currentElement;
+        }
+        return 0;
+    }
+    return traverseMatchingElementsForwardToOffset(this, offset, currentElement, currentOffset, root);
+}
+
 Node* HTMLCollection::namedItem(const AtomicString& name) const
 {
     // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp
@@ -487,21 +623,25 @@
     // object with a matching name attribute, but only on those elements
     // that are allowed a name attribute.
 
+    ContainerNode* root = rootContainerNode();
+    if (!root)
+        return 0;
+
     unsigned arrayOffset = 0;
     unsigned i = 0;
-    for (Node* e = itemAfter(arrayOffset, 0); e; e = itemAfter(arrayOffset, e)) {
-        if (checkForNameMatch(toElement(e), /* checkName */ false, name)) {
-            setItemCache(e, i, arrayOffset);
-            return e;
+    for (Element* element = traverseFirstElement(arrayOffset, root); element; element = traverseNextElement(arrayOffset, element, root)) {
+        if (checkForNameMatch(element, /* checkName */ false, name)) {
+            setItemCache(element, i, arrayOffset);
+            return element;
         }
         i++;
     }
 
     i = 0;
-    for (Node* e = itemAfter(arrayOffset, 0); e; e = itemAfter(arrayOffset, e)) {
-        if (checkForNameMatch(toElement(e), /* checkName */ true, name)) {
-            setItemCache(e, i, arrayOffset);
-            return toElement(e);
+    for (Element* element = traverseFirstElement(arrayOffset, root); element; element = traverseNextElement(arrayOffset, element, root)) {
+        if (checkForNameMatch(element, /* checkName */ true, name)) {
+            setItemCache(element, i, arrayOffset);
+            return element;
         }
         i++;
     }
@@ -514,17 +654,21 @@
     if (hasNameCache())
         return;
 
+    ContainerNode* root = rootContainerNode();
+    if (!root)
+        return;
+
     unsigned arrayOffset = 0;
-    for (Node* node = itemAfter(arrayOffset, 0); node; node = itemAfter(arrayOffset, node)) {
-        if (!node->isHTMLElement())
+    for (Element* element = traverseFirstElement(arrayOffset, root); element; element = traverseNextElement(arrayOffset, element, root)) {
+        if (!element->isHTMLElement())
             continue;
-        HTMLElement* e = toHTMLElement(node);
-        const AtomicString& idAttrVal = e->getIdAttribute();
-        const AtomicString& nameAttrVal = e->getNameAttribute();
+        HTMLElement* htmlElement = toHTMLElement(element);
+        const AtomicString& idAttrVal = htmlElement->getIdAttribute();
+        const AtomicString& nameAttrVal = htmlElement->getNameAttribute();
         if (!idAttrVal.isEmpty())
-            appendIdCache(idAttrVal, e);
-        if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && (type() != DocAll || nameShouldBeVisibleInDocumentAll(e)))
-            appendNameCache(nameAttrVal, e);
+            appendIdCache(idAttrVal, htmlElement);
+        if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && (type() != DocAll || nameShouldBeVisibleInDocumentAll(htmlElement)))
+            appendNameCache(nameAttrVal, htmlElement);
     }
 
     setHasNameCache();

Modified: trunk/Source/WebCore/html/HTMLCollection.h (138194 => 138195)


--- trunk/Source/WebCore/html/HTMLCollection.h	2012-12-19 21:59:25 UTC (rev 138194)
+++ trunk/Source/WebCore/html/HTMLCollection.h	2012-12-19 22:39:28 UTC (rev 138195)
@@ -64,6 +64,9 @@
 
     virtual Element* virtualItemAfter(unsigned& offsetInArray, Element*) const;
 
+    Element* traverseFirstElement(unsigned& offsetInArray, ContainerNode* root) const;
+    Element* traverseForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, unsigned& offsetInArray, ContainerNode* root) const;
+
 protected:
     HTMLCollection(Node* base, CollectionType, ItemAfterOverrideType);
 
@@ -77,6 +80,7 @@
 
 private:
     bool checkForNameMatch(Element*, bool checkName, const AtomicString& name) const;
+    Element* traverseNextElement(unsigned& offsetInArray, Element* previous, ContainerNode* root) const;
 
     virtual bool isLiveNodeList() const OVERRIDE { ASSERT_NOT_REACHED(); return true; }
 
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to