ChildNodeList should not be LiveNodeList
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 3 Nov 2013 19:52:40 +0000 (19:52 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 3 Nov 2013 19:52:40 +0000 (19:52 +0000)
https://bugs.webkit.org/show_bug.cgi?id=123708

Reviewed by Sam Weinig.

ChildNodeList is a poor fit to be a LiveNodeList. It is heavily special-cased. It is also
the only subtype that returns non-Elements thus preventing tightening.

* bindings/js/JSNodeListCustom.cpp:
(WebCore::JSNodeListOwner::isReachableFromOpaqueRoots):

    Support new types.

* dom/ChildNodeList.cpp:
(WebCore::EmptyNodeList::~EmptyNodeList):
(WebCore::ChildNodeList::ChildNodeList):
(WebCore::ChildNodeList::~ChildNodeList):
(WebCore::ChildNodeList::length):
(WebCore::childFromFirst):
(WebCore::childFromLast):
(WebCore::ChildNodeList::nodeBeforeCached):
(WebCore::ChildNodeList::nodeAfterCached):
(WebCore::ChildNodeList::item):
(WebCore::ChildNodeList::namedItem):
(WebCore::ChildNodeList::invalidateCache):

    Implement the same caching optimizations as LiveNodeList with tighter, less generic code.

* dom/ChildNodeList.h:

    Inherit ChildNodeList directly from NodeList.

    Add new EmptyNodeList type. This is only ever used if NodeList is requested for a non-container node.
    It allows tighter typing in ChildNodeList.

* dom/LiveNodeList.cpp:
(WebCore::LiveNodeList::namedItem):
* dom/LiveNodeList.h:
(WebCore::LiveNodeListBase::LiveNodeListBase):
(WebCore::LiveNodeListBase::~LiveNodeListBase):
(WebCore::LiveNodeList::LiveNodeList):

    Remove ChildNodeList specific code and branches.

* dom/Node.cpp:
(WebCore::Node::childNodes):

    Return EmptyNodeList for non-containers.

* dom/NodeList.h:
(WebCore::NodeList::~NodeList):
(WebCore::NodeList::isLiveNodeList):
(WebCore::NodeList::isChildNodeList):
(WebCore::NodeList::isEmptyNodeList):

    For isReachableFromOpaqueRoots.

* dom/NodeRareData.h:
(WebCore::NodeListsNodeData::ensureChildNodeList):
(WebCore::NodeListsNodeData::removeChildNodeList):
(WebCore::NodeListsNodeData::ensureEmptyChildNodeList):
(WebCore::NodeListsNodeData::removeEmptyChildNodeList):
(WebCore::NodeListsNodeData::NodeListsNodeData):
(WebCore::NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList):

    EmptyNodeList support.

* html/CollectionType.h:
* html/HTMLCollection.cpp:
(WebCore::shouldOnlyIncludeDirectChildren):
(WebCore::rootTypeFromCollectionType):
(WebCore::invalidationTypeExcludingIdAndNameAttributes):
(WebCore::isMatchingElement):
(WebCore::LiveNodeListBase::itemBefore):
(WebCore::LiveNodeListBase::traverseLiveNodeListFirstElement):
(WebCore::LiveNodeListBase::traverseLiveNodeListForwardToOffset):
(WebCore::LiveNodeListBase::item):
(WebCore::LiveNodeListBase::itemBeforeOrAfterCachedItem):

    Remove ChildNodeList specific code and branches.

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

Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSNodeListCustom.cpp
Source/WebCore/dom/ChildNodeList.cpp
Source/WebCore/dom/ChildNodeList.h
Source/WebCore/dom/LiveNodeList.cpp
Source/WebCore/dom/LiveNodeList.h
Source/WebCore/dom/Node.cpp
Source/WebCore/dom/NodeList.h
Source/WebCore/dom/NodeRareData.h
Source/WebCore/html/CollectionType.h
Source/WebCore/html/HTMLCollection.cpp

index bd97686..a518a66 100644 (file)
@@ -1,3 +1,86 @@
+2013-11-03  Antti Koivisto  <antti@apple.com>
+
+        ChildNodeList should not be LiveNodeList
+        https://bugs.webkit.org/show_bug.cgi?id=123708
+
+        Reviewed by Sam Weinig.
+
+        ChildNodeList is a poor fit to be a LiveNodeList. It is heavily special-cased. It is also
+        the only subtype that returns non-Elements thus preventing tightening.
+
+        * bindings/js/JSNodeListCustom.cpp:
+        (WebCore::JSNodeListOwner::isReachableFromOpaqueRoots):
+        
+            Support new types.
+
+        * dom/ChildNodeList.cpp:
+        (WebCore::EmptyNodeList::~EmptyNodeList):
+        (WebCore::ChildNodeList::ChildNodeList):
+        (WebCore::ChildNodeList::~ChildNodeList):
+        (WebCore::ChildNodeList::length):
+        (WebCore::childFromFirst):
+        (WebCore::childFromLast):
+        (WebCore::ChildNodeList::nodeBeforeCached):
+        (WebCore::ChildNodeList::nodeAfterCached):
+        (WebCore::ChildNodeList::item):
+        (WebCore::ChildNodeList::namedItem):
+        (WebCore::ChildNodeList::invalidateCache):
+        
+            Implement the same caching optimizations as LiveNodeList with tighter, less generic code.
+
+        * dom/ChildNodeList.h:
+        
+            Inherit ChildNodeList directly from NodeList.
+
+            Add new EmptyNodeList type. This is only ever used if NodeList is requested for a non-container node.
+            It allows tighter typing in ChildNodeList.
+
+        * dom/LiveNodeList.cpp:
+        (WebCore::LiveNodeList::namedItem):
+        * dom/LiveNodeList.h:
+        (WebCore::LiveNodeListBase::LiveNodeListBase):
+        (WebCore::LiveNodeListBase::~LiveNodeListBase):
+        (WebCore::LiveNodeList::LiveNodeList):
+        
+            Remove ChildNodeList specific code and branches.
+
+        * dom/Node.cpp:
+        (WebCore::Node::childNodes):
+        
+            Return EmptyNodeList for non-containers.
+
+        * dom/NodeList.h:
+        (WebCore::NodeList::~NodeList):
+        (WebCore::NodeList::isLiveNodeList):
+        (WebCore::NodeList::isChildNodeList):
+        (WebCore::NodeList::isEmptyNodeList):
+        
+            For isReachableFromOpaqueRoots.
+
+        * dom/NodeRareData.h:
+        (WebCore::NodeListsNodeData::ensureChildNodeList):
+        (WebCore::NodeListsNodeData::removeChildNodeList):
+        (WebCore::NodeListsNodeData::ensureEmptyChildNodeList):
+        (WebCore::NodeListsNodeData::removeEmptyChildNodeList):
+        (WebCore::NodeListsNodeData::NodeListsNodeData):
+        (WebCore::NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList):
+        
+            EmptyNodeList support.
+
+        * html/CollectionType.h:
+        * html/HTMLCollection.cpp:
+        (WebCore::shouldOnlyIncludeDirectChildren):
+        (WebCore::rootTypeFromCollectionType):
+        (WebCore::invalidationTypeExcludingIdAndNameAttributes):
+        (WebCore::isMatchingElement):
+        (WebCore::LiveNodeListBase::itemBefore):
+        (WebCore::LiveNodeListBase::traverseLiveNodeListFirstElement):
+        (WebCore::LiveNodeListBase::traverseLiveNodeListForwardToOffset):
+        (WebCore::LiveNodeListBase::item):
+        (WebCore::LiveNodeListBase::itemBeforeOrAfterCachedItem):
+        
+            Remove ChildNodeList specific code and branches.
+
 2013-11-03  Patrick Gansterer  <paroga@webkit.org>
 
         [WINCE] Replace OwnPtr with GDIObject
index 62928db..29e9e88 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "JSNodeList.h"
 
+#include "ChildNodeList.h"
 #include "JSNode.h"
 #include "LiveNodeList.h"
 #include "Node.h"
@@ -41,9 +42,13 @@ bool JSNodeListOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handl
     JSNodeList* jsNodeList = jsCast<JSNodeList*>(handle.get().asCell());
     if (!jsNodeList->hasCustomProperties())
         return false;
-    if (!jsNodeList->impl().isLiveNodeList())
-        return false;
-    return visitor.containsOpaqueRoot(root(static_cast<LiveNodeList&>(jsNodeList->impl()).ownerNode()));
+    if (jsNodeList->impl().isLiveNodeList())
+        return visitor.containsOpaqueRoot(root(static_cast<LiveNodeList&>(jsNodeList->impl()).ownerNode()));
+    if (jsNodeList->impl().isChildNodeList())
+        return visitor.containsOpaqueRoot(root(static_cast<ChildNodeList&>(jsNodeList->impl()).ownerNode()));
+    if (jsNodeList->impl().isEmptyNodeList())
+        return visitor.containsOpaqueRoot(root(static_cast<EmptyNodeList&>(jsNodeList->impl()).ownerNode()));
+    return false;
 }
 
 bool JSNodeList::canGetItemsForName(ExecState*, NodeList* impl, PropertyName propertyName)
index 56804a3..a0b8115 100644 (file)
 #include "ChildNodeList.h"
 
 #include "Element.h"
+#include "ElementIterator.h"
 #include "NodeRareData.h"
 
 namespace WebCore {
 
-ChildNodeList::ChildNodeList(Node& node)
-    : LiveNodeList(node, ChildNodeListType, DoNotInvalidateOnAttributeChanges)
+EmptyNodeList::~EmptyNodeList()
+{
+    m_owner.get().nodeLists()->removeEmptyChildNodeList(this);
+}
+
+ChildNodeList::ChildNodeList(ContainerNode& parent)
+    : m_parent(parent)
+    , m_cachedLength(0)
+    , m_cachedLengthValid(false)
+    , m_cachedCurrentPosition(0)
+    , m_cachedCurrentNode(nullptr)
 {
 }
 
 ChildNodeList::~ChildNodeList()
 {
-    ownerNode().nodeLists()->removeChildNodeList(this);
+    m_parent.get().nodeLists()->removeChildNodeList(this);
+}
+
+unsigned ChildNodeList::length() const
+{
+    if (!m_cachedLengthValid) {
+        m_cachedLength = m_parent->childNodeCount();
+        m_cachedLengthValid = true;
+    }
+    return m_cachedLength;
+}
+
+static Node* childFromFirst(const ContainerNode& parent, unsigned delta)
+{
+    Node* child = parent.firstChild();
+    for (; delta && child; --delta)
+        child = child->nextSibling();
+    return child;
+}
+
+static Node* childFromLast(const ContainerNode& parent, unsigned delta)
+{
+    Node* child = parent.lastChild();
+    for (; delta; --delta)
+        child = child->previousSibling();
+    ASSERT(child);
+    return child;
+}
+
+Node* ChildNodeList::nodeBeforeCached(unsigned index) const
+{
+    ASSERT(m_cachedCurrentNode);
+    ASSERT(index < m_cachedCurrentPosition);
+    if (index < m_cachedCurrentPosition - index) {
+        m_cachedCurrentNode = childFromFirst(m_parent.get(), index);
+        m_cachedCurrentPosition = index;
+        return m_cachedCurrentNode;
+    }
+    while (m_cachedCurrentNode && m_cachedCurrentPosition > index) {
+        m_cachedCurrentNode = m_cachedCurrentNode->previousSibling();
+        --m_cachedCurrentPosition;
+    }
+    return m_cachedCurrentNode;
+}
+
+Node* ChildNodeList::nodeAfterCached(unsigned index) const
+{
+    ASSERT(m_cachedCurrentNode);
+    ASSERT(index > m_cachedCurrentPosition);
+    ASSERT(!m_cachedLengthValid || index < m_cachedLength);
+    if (m_cachedLengthValid && m_cachedLength - index < index - m_cachedCurrentPosition) {
+        m_cachedCurrentNode = childFromLast(m_parent.get(), m_cachedLength - index - 1);
+        m_cachedCurrentPosition = index;
+        return m_cachedCurrentNode;
+    }
+    while (m_cachedCurrentNode && m_cachedCurrentPosition < index) {
+        m_cachedCurrentNode = m_cachedCurrentNode->nextSibling();
+        ++m_cachedCurrentPosition;
+    }
+    if (!m_cachedCurrentNode && !m_cachedLengthValid) {
+        m_cachedLength = m_cachedCurrentPosition;
+        m_cachedLengthValid = true;
+    }
+    return m_cachedCurrentNode;
+}
+
+Node* ChildNodeList::item(unsigned index) const
+{
+    if (m_cachedLengthValid && index >= m_cachedLength)
+        return nullptr;
+    if (m_cachedCurrentNode) {
+        if (index > m_cachedCurrentPosition)
+            return nodeAfterCached(index);
+        if (index < m_cachedCurrentPosition)
+            return nodeBeforeCached(index);
+        return m_cachedCurrentNode;
+    }
+    if (m_cachedLengthValid && m_cachedLength - index < index)
+        m_cachedCurrentNode = childFromLast(m_parent.get(), m_cachedLength - index - 1);
+    else
+        m_cachedCurrentNode = childFromFirst(m_parent.get(), index);
+    m_cachedCurrentPosition = index;
+    return m_cachedCurrentNode;
+}
+
+Node* ChildNodeList::namedItem(const AtomicString& name) const
+{
+    // FIXME: According to the spec child node list should not have namedItem().
+    if (m_parent.get().inDocument()) {
+        Element* element = m_parent.get().treeScope().getElementById(name);
+        if (element && element->parentNode() == &m_parent.get())
+            return element;
+        if (!element || !m_parent.get().treeScope().containsMultipleElementsWithId(name))
+            return nullptr;
+    }
+    auto children = elementChildren(m_parent.get());
+    for (auto it = children.begin(), end = children.end(); it != end; ++it) {
+        auto& element = *it;
+        if (element.hasID() && element.idForStyleResolution() == name)
+            return const_cast<Element*>(&element);
+    }
+    return nullptr;
 }
 
-bool ChildNodeList::nodeMatches(Element* testNode) const
+void ChildNodeList::invalidateCache()
 {
-    // This function will be called only by LiveNodeList::namedItem,
-    // for an element that was located with getElementById.
-    return testNode->parentNode() == &rootNode();
+    m_cachedCurrentNode = nullptr;
+    m_cachedLengthValid = false;
 }
 
 } // namespace WebCore
index e78b723..d29c81f 100644 (file)
 #ifndef ChildNodeList_h
 #define ChildNodeList_h
 
-#include "LiveNodeList.h"
-#include <wtf/PassRefPtr.h>
+#include "NodeList.h"
+#include <wtf/Ref.h>
+#include <wtf/RefPtr.h>
 
 namespace WebCore {
 
-    class ChildNodeList : public LiveNodeList {
-    public:
-        static PassRefPtr<ChildNodeList> create(Node& rootNode)
-        {
-            return adoptRef(new ChildNodeList(rootNode));
-        }
+class ContainerNode;
 
-        virtual ~ChildNodeList();
+class EmptyNodeList FINAL : public NodeList {
+public:
+    static PassRefPtr<EmptyNodeList> create(Node& owner)
+    {
+        return adoptRef(new EmptyNodeList(owner));
+    }
+    virtual ~EmptyNodeList();
 
-    protected:
-        explicit ChildNodeList(Node& rootNode);
+    Node& ownerNode() { return m_owner.get(); }
 
-        virtual bool nodeMatches(Element*) const OVERRIDE;
-    };
+private:
+    explicit EmptyNodeList(Node& owner) : m_owner(owner) { }
+
+    virtual unsigned length() const OVERRIDE { return 0; }
+    virtual Node* item(unsigned) const OVERRIDE { return nullptr; }
+    virtual Node* namedItem(const AtomicString&) const OVERRIDE { return nullptr; }
+
+    virtual bool isEmptyNodeList() const OVERRIDE { return true; }
+
+    Ref<Node> m_owner;
+};
+
+class ChildNodeList FINAL : public NodeList {
+public:
+    static PassRefPtr<ChildNodeList> create(ContainerNode& parent)
+    {
+        return adoptRef(new ChildNodeList(parent));
+    }
+
+    virtual ~ChildNodeList();
+
+    ContainerNode& ownerNode() { return m_parent.get(); }
+
+    void invalidateCache();
+
+private:
+    explicit ChildNodeList(ContainerNode& parent);
+
+    virtual unsigned length() const OVERRIDE;
+    virtual Node* item(unsigned index) const OVERRIDE;
+    virtual Node* namedItem(const AtomicString&) const OVERRIDE;
+
+    virtual bool isChildNodeList() const OVERRIDE { return true; }
+
+    Node* nodeBeforeCached(unsigned) const;
+    Node* nodeAfterCached(unsigned) const;
+
+    Ref<ContainerNode> m_parent;
+    mutable unsigned m_cachedLength : 31;
+    mutable unsigned m_cachedLengthValid : 1;
+    mutable unsigned m_cachedCurrentPosition;
+    mutable Node* m_cachedCurrentNode;
+};
 
 } // namespace WebCore
 
index 80c22a4..f9fe484 100644 (file)
@@ -71,6 +71,7 @@ void LiveNodeListBase::invalidateIdNameCacheMaps() const
 
 Node* LiveNodeList::namedItem(const AtomicString& elementId) const
 {
+    // FIXME: Why doesn't this look into the name attribute like HTMLCollection::namedItem does?
     Node& rootNode = this->rootNode();
 
     if (rootNode.inDocument()) {
index 5970b08..3601ed2 100644 (file)
@@ -67,14 +67,12 @@ public:
         ASSERT(m_collectionType == static_cast<unsigned>(collectionType));
         ASSERT(!m_overridesItemAfter || !isNodeList(collectionType));
 
-        if (collectionType != ChildNodeListType)
-            document().registerNodeList(this);
+        document().registerNodeList(this);
     }
 
     virtual ~LiveNodeListBase()
     {
-        if (type() != ChildNodeListType)
-            document().unregisterNodeList(this);
+        document().unregisterNodeList(this);
     }
 
     // DOM API
@@ -136,7 +134,6 @@ protected:
 
 private:
     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;
@@ -188,8 +185,7 @@ ALWAYS_INLINE bool LiveNodeListBase::shouldInvalidateTypeOnAttributeChange(NodeL
 class LiveNodeList : public LiveNodeListBase {
 public:
     LiveNodeList(Node& ownerNode, CollectionType collectionType, NodeListInvalidationType invalidationType, NodeListRootType rootType = NodeListIsRootedAtNode)
-        : LiveNodeListBase(ownerNode, rootType, invalidationType, collectionType == ChildNodeListType,
-        collectionType, DoesNotOverrideItemAfter)
+        : LiveNodeListBase(ownerNode, rootType, invalidationType, /*shouldOnlyIncludeDirectChildren*/ false, collectionType, DoesNotOverrideItemAfter)
     { }
 
     virtual Node* namedItem(const AtomicString&) const OVERRIDE;
index 4ddd5b7..ffac827 100644 (file)
@@ -426,7 +426,9 @@ void Node::setNodeValue(const String& /*nodeValue*/, ExceptionCode& ec)
 
 PassRefPtr<NodeList> Node::childNodes()
 {
-    return ensureRareData().ensureNodeLists().ensureChildNodeList(*this);
+    if (isContainerNode())
+        return ensureRareData().ensureNodeLists().ensureChildNodeList(toContainerNode(*this));
+    return ensureRareData().ensureNodeLists().ensureEmptyChildNodeList(*this);
 }
 
 Node *Node::lastDescendant() const
index a768e62..cd82115 100644 (file)
 
 namespace WebCore {
 
-    class Node;
-
-    class NodeList : public ScriptWrappable, public RefCounted<NodeList> {
-    public:
-        virtual ~NodeList() { }
-
-        // DOM methods & attributes for NodeList
-        virtual unsigned length() const = 0;
-        virtual Node* item(unsigned index) const = 0;
-        virtual Node* namedItem(const AtomicString&) const = 0;
-
-        // Other methods (not part of DOM)
-        virtual bool isLiveNodeList() const { return false; }
-    };
+class Node;
+
+class NodeList : public ScriptWrappable, public RefCounted<NodeList> {
+public:
+    virtual ~NodeList() { }
+
+    // DOM methods & attributes for NodeList
+    virtual unsigned length() const = 0;
+    virtual Node* item(unsigned index) const = 0;
+    virtual Node* namedItem(const AtomicString&) const = 0;
+
+    // Other methods (not part of DOM)
+    virtual bool isLiveNodeList() const { return false; }
+    virtual bool isChildNodeList() const { return false; }
+    virtual bool isEmptyNodeList() const { return false; }
+};
 
 } // namespace WebCore
 
index afc76fe..9d8ff61 100644 (file)
@@ -55,8 +55,9 @@ public:
             m_childNodeList->invalidateCache();
     }
 
-    PassRefPtr<ChildNodeList> ensureChildNodeList(Node& node)
+    PassRefPtr<ChildNodeList> ensureChildNodeList(ContainerNode& node)
     {
+        ASSERT(!m_emptyChildNodeList);
         if (m_childNodeList)
             return m_childNodeList;
         RefPtr<ChildNodeList> list = ChildNodeList::create(node);
@@ -66,10 +67,28 @@ public:
 
     void removeChildNodeList(ChildNodeList* list)
     {
-        ASSERT(m_childNodeList = list);
+        ASSERT(m_childNodeList == list);
         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
             return;
-        m_childNodeList = 0;
+        m_childNodeList = nullptr;
+    }
+
+    PassRefPtr<EmptyNodeList> ensureEmptyChildNodeList(Node& node)
+    {
+        ASSERT(!m_childNodeList);
+        if (m_emptyChildNodeList)
+            return m_emptyChildNodeList;
+        RefPtr<EmptyNodeList> list = EmptyNodeList::create(node);
+        m_emptyChildNodeList = list.get();
+        return list.release();
+    }
+
+    void removeEmptyChildNodeList(EmptyNodeList* list)
+    {
+        ASSERT(m_emptyChildNodeList == list);
+        if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
+            return;
+        m_emptyChildNodeList = nullptr;
     }
 
     template <typename StringType>
@@ -213,7 +232,8 @@ public:
 
 private:
     NodeListsNodeData()
-        : m_childNodeList(0)
+        : m_childNodeList(nullptr)
+        , m_emptyChildNodeList(nullptr)
     { }
 
     std::pair<unsigned char, AtomicString> namedNodeListKey(CollectionType type, const AtomicString& name)
@@ -228,9 +248,10 @@ private:
 
     bool deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node&);
 
-    // FIXME: m_childNodeList should be merged into m_atomicNameCaches or at least be shared with HTMLCollection returned by Element::children
-    // but it's tricky because invalidateCaches shouldn't invalidate this cache and adoptTreeScope shouldn't call registerNodeList or unregisterNodeList.
+    // These two are currently mutually exclusive and could be unioned. Not very important as this class is large anyway.
     ChildNodeList* m_childNodeList;
+    EmptyNodeList* m_emptyChildNodeList;
+
     NodeListAtomicNameCacheMap m_atomicNameCaches;
     NodeListNameCacheMap m_nameCaches;
     TagNodeListCacheNS m_tagNodeListCacheNS;
@@ -298,7 +319,7 @@ private:
 inline bool NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node& ownerNode)
 {
     ASSERT(ownerNode.nodeLists() == this);
-    if ((m_childNodeList ? 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() != 1)
         return false;
     ownerNode.clearNodeLists();
     return true;
index c576c11..3429e39 100644 (file)
@@ -53,7 +53,6 @@ enum CollectionType {
     FormControls,
 
     // Live NodeList.
-    ChildNodeListType,
     ClassNodeListType,
     NameNodeListType,
     TagNodeListType,
@@ -62,7 +61,7 @@ enum CollectionType {
     LabelsNodeListType,
 };
 
-static const CollectionType FirstNodeListType = ChildNodeListType;
+static const CollectionType FirstNodeListType = ClassNodeListType;
 
 inline bool isNodeList(CollectionType type)
 {
index ba53d56..4db56a7 100644 (file)
@@ -63,7 +63,6 @@ static bool shouldOnlyIncludeDirectChildren(CollectionType type)
     case TSectionRows:
     case TableTBodies:
         return true;
-    case ChildNodeListType:
     case ClassNodeListType:
     case NameNodeListType:
     case TagNodeListType:
@@ -101,7 +100,6 @@ static NodeListRootType rootTypeFromCollectionType(CollectionType type)
     case DataListOptions:
     case MapAreas:
         return NodeListIsRootedAtNode;
-    case ChildNodeListType:
     case ClassNodeListType:
     case NameNodeListType:
     case TagNodeListType:
@@ -145,7 +143,6 @@ static NodeListInvalidationType invalidationTypeExcludingIdAndNameAttributes(Col
         return InvalidateOnIdNameAttrChange;
     case FormControls:
         return InvalidateForFormControls;
-    case ChildNodeListType:
     case ClassNodeListType:
     case NameNodeListType:
     case TagNodeListType:
@@ -228,7 +225,6 @@ template <> inline bool isMatchingElement(const HTMLCollection* htmlCollection,
         return static_cast<const WindowNameCollection*>(htmlCollection)->nodeMatches(element);
     case FormControls:
     case TableRows:
-    case ChildNodeListType:
     case ClassNodeListType:
     case NameNodeListType:
     case TagNodeListType:
@@ -290,8 +286,6 @@ ALWAYS_INLINE Node* LiveNodeListBase::itemBefore(Node* previous) const
     else
         current = lastNode(rootNode(), shouldOnlyIncludeDirectChildren());
 
-    if (type() == ChildNodeListType)
-        return current;
     return iterateForPreviousNode(current);
 }
 
@@ -324,23 +318,10 @@ inline Element* traverseMatchingElementsForwardToOffset(const NodeListType* node
     return 0;
 }
 
-// FIXME: This should be in ChildNodeList
-inline Node* LiveNodeListBase::traverseChildNodeListForwardToOffset(unsigned offset, Node* currentNode, unsigned& currentOffset) const
-{
-    ASSERT(type() == ChildNodeListType);
-    ASSERT_WITH_SECURITY_IMPLICATION(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)
@@ -352,7 +333,6 @@ inline Element* LiveNodeListBase::traverseLiveNodeListFirstElement(ContainerNode
 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)
@@ -424,9 +404,7 @@ Node* LiveNodeListBase::item(unsigned offset) const
     } else if (!isItemCacheValid() || isFirstItemCloserThanCachedItem(offset) || (overridesItemAfter() && offset < cachedItemOffset())) {
         unsigned offsetInArray = 0;
         Node* firstItem;
-        if (type() == ChildNodeListType)
-            firstItem = root->firstChild();
-        else if (isNodeList(type()))
+        if (isNodeList(type()))
             firstItem = traverseLiveNodeListFirstElement(root);
         else
             firstItem = static_cast<const HTMLCollection*>(this)->traverseFirstElement(offsetInArray, root);
@@ -467,9 +445,7 @@ inline Node* LiveNodeListBase::itemBeforeOrAfterCachedItem(unsigned offset, Cont
     }
 
     unsigned offsetInArray = 0;
-    if (type() == ChildNodeListType)
-        currentItem = traverseChildNodeListForwardToOffset(offset, currentItem, currentOffset);
-    else if (isNodeList(type()))
+    if (isNodeList(type()))
         currentItem = traverseLiveNodeListForwardToOffset(offset, toElement(currentItem), currentOffset, root);
     else
         currentItem = static_cast<const HTMLCollection*>(this)->traverseForwardToOffset(offset, toElement(currentItem), currentOffset, offsetInArray, root);