Move array position caching out from HTMLCollection
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 6 Nov 2013 18:18:48 +0000 (18:18 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 6 Nov 2013 18:18:48 +0000 (18:18 +0000)
https://bugs.webkit.org/show_bug.cgi?id=123895

Reviewed by Darin Adler.

This caching complicates the logic but is used by a single subclass
(HTMLFormControlsCollection) only. The subclass can do the caching itself.

* html/HTMLAllCollection.cpp:
(WebCore::HTMLAllCollection::HTMLAllCollection):
* html/HTMLCollection.cpp:
(WebCore::HTMLCollection::HTMLCollection):
(WebCore::HTMLCollection::create):
(WebCore::HTMLCollection::item):
(WebCore::HTMLCollection::elementBeforeOrAfterCachedElement):
(WebCore::HTMLCollection::firstElement):

    Renamed from traverseFirstElement.

(WebCore::HTMLCollection::traverseForwardToOffset):
(WebCore::HTMLCollection::invalidateCache):

    Make cache invalidation virtual so we can clear HTMLTableRowsCollection index cache.

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

    Use traverseForwardToOffset instead traverseNextElement. This allows removal of traverseNextElement.

* html/HTMLCollection.h:
(WebCore::HTMLCollection::usesCustomForwardOnlyTraversal):

    Renamed the enum and the accessor to be more informative.

(WebCore::HTMLCollection::setCachedElement):
(WebCore::HTMLCollection::customElementAfter):

    Renamed from virtualItemAfter.

* html/HTMLFormControlsCollection.cpp:
(WebCore::HTMLFormControlsCollection::HTMLFormControlsCollection):
(WebCore::findFormAssociatedElement):
(WebCore::HTMLFormControlsCollection::customElementAfter):

    Move the array position caching logic here.

(WebCore::HTMLFormControlsCollection::invalidateCache):
* html/HTMLFormControlsCollection.h:
* html/HTMLNameCollection.cpp:
(WebCore::HTMLNameCollection::HTMLNameCollection):
* html/HTMLOptionsCollection.cpp:
(WebCore::HTMLOptionsCollection::HTMLOptionsCollection):
* html/HTMLTableRowsCollection.cpp:
(WebCore::HTMLTableRowsCollection::HTMLTableRowsCollection):
(WebCore::HTMLTableRowsCollection::customElementAfter):
* html/HTMLTableRowsCollection.h:

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

Source/WebCore/ChangeLog
Source/WebCore/html/HTMLAllCollection.cpp
Source/WebCore/html/HTMLCollection.cpp
Source/WebCore/html/HTMLCollection.h
Source/WebCore/html/HTMLFormControlsCollection.cpp
Source/WebCore/html/HTMLFormControlsCollection.h
Source/WebCore/html/HTMLNameCollection.cpp
Source/WebCore/html/HTMLOptionsCollection.cpp
Source/WebCore/html/HTMLTableRowsCollection.cpp
Source/WebCore/html/HTMLTableRowsCollection.h

index e227f5c..d2711e5 100644 (file)
@@ -1,3 +1,62 @@
+2013-11-06  Antti Koivisto  <antti@apple.com>
+
+        Move array position caching out from HTMLCollection
+        https://bugs.webkit.org/show_bug.cgi?id=123895
+
+        Reviewed by Darin Adler.
+
+        This caching complicates the logic but is used by a single subclass
+        (HTMLFormControlsCollection) only. The subclass can do the caching itself.
+
+        * html/HTMLAllCollection.cpp:
+        (WebCore::HTMLAllCollection::HTMLAllCollection):
+        * html/HTMLCollection.cpp:
+        (WebCore::HTMLCollection::HTMLCollection):
+        (WebCore::HTMLCollection::create):
+        (WebCore::HTMLCollection::item):
+        (WebCore::HTMLCollection::elementBeforeOrAfterCachedElement):
+        (WebCore::HTMLCollection::firstElement):
+        
+            Renamed from traverseFirstElement.
+
+        (WebCore::HTMLCollection::traverseForwardToOffset):
+        (WebCore::HTMLCollection::invalidateCache):
+        
+            Make cache invalidation virtual so we can clear HTMLTableRowsCollection index cache.
+
+        (WebCore::HTMLCollection::namedItem):
+        (WebCore::HTMLCollection::updateNameCache):
+        
+            Use traverseForwardToOffset instead traverseNextElement. This allows removal of traverseNextElement.
+
+        * html/HTMLCollection.h:
+        (WebCore::HTMLCollection::usesCustomForwardOnlyTraversal):
+        
+            Renamed the enum and the accessor to be more informative.
+
+        (WebCore::HTMLCollection::setCachedElement):
+        (WebCore::HTMLCollection::customElementAfter):
+        
+            Renamed from virtualItemAfter.
+
+        * html/HTMLFormControlsCollection.cpp:
+        (WebCore::HTMLFormControlsCollection::HTMLFormControlsCollection):
+        (WebCore::findFormAssociatedElement):
+        (WebCore::HTMLFormControlsCollection::customElementAfter):
+        
+            Move the array position caching logic here.
+
+        (WebCore::HTMLFormControlsCollection::invalidateCache):
+        * html/HTMLFormControlsCollection.h:
+        * html/HTMLNameCollection.cpp:
+        (WebCore::HTMLNameCollection::HTMLNameCollection):
+        * html/HTMLOptionsCollection.cpp:
+        (WebCore::HTMLOptionsCollection::HTMLOptionsCollection):
+        * html/HTMLTableRowsCollection.cpp:
+        (WebCore::HTMLTableRowsCollection::HTMLTableRowsCollection):
+        (WebCore::HTMLTableRowsCollection::customElementAfter):
+        * html/HTMLTableRowsCollection.h:
+
 2013-11-06  Michał Pakuła vel Rutka  <m.pakula@samsung.com>
 
         [ATK] accessibility/title-ui-element-correctness.html fails
index 41b0547..1812ab1 100644 (file)
@@ -36,7 +36,7 @@ PassRef<HTMLAllCollection> HTMLAllCollection::create(Document& document, Collect
 }
 
 HTMLAllCollection::HTMLAllCollection(Document& document, CollectionType type)
-    : HTMLCollection(document, type, DoesNotOverrideItemAfter)
+    : HTMLCollection(document, type)
 {
 }
 
index 28a8e0d..f0af4f5 100644 (file)
@@ -131,7 +131,7 @@ static NodeListInvalidationType invalidationTypeExcludingIdAndNameAttributes(Col
     return DoNotInvalidateOnAttributeChanges;
 }
 
-HTMLCollection::HTMLCollection(ContainerNode& ownerNode, CollectionType type, ItemAfterOverrideType itemAfterOverrideType)
+HTMLCollection::HTMLCollection(ContainerNode& ownerNode, CollectionType type, ElementTraversalType traversalType)
     : m_ownerNode(ownerNode)
     , m_cachedElement(nullptr)
     , m_isLengthCacheValid(false)
@@ -141,7 +141,7 @@ HTMLCollection::HTMLCollection(ContainerNode& ownerNode, CollectionType type, It
     , m_shouldOnlyIncludeDirectChildren(shouldOnlyIncludeDirectChildren(type))
     , m_isNameCacheValid(false)
     , m_collectionType(type)
-    , m_overridesItemAfter(itemAfterOverrideType == OverridesItemAfter)
+    , m_usesCustomForwardOnlyTraversal(traversalType == CustomForwardOnlyTraversal)
     , m_isItemRefElementsCacheValid(false)
 {
     ASSERT(m_rootType == static_cast<unsigned>(rootTypeFromCollectionType(type)));
@@ -153,7 +153,7 @@ HTMLCollection::HTMLCollection(ContainerNode& ownerNode, CollectionType type, It
 
 PassRefPtr<HTMLCollection> HTMLCollection::create(ContainerNode& base, CollectionType type)
 {
-    return adoptRef(new HTMLCollection(base, type, DoesNotOverrideItemAfter));
+    return adoptRef(new HTMLCollection(base, type));
 }
 
 HTMLCollection::~HTMLCollection()
@@ -305,15 +305,6 @@ bool ALWAYS_INLINE HTMLCollection::isFirstItemCloserThanCachedItem(unsigned offs
     return offset < distanceFromCachedItem;
 }
 
-ALWAYS_INLINE void HTMLCollection::setCachedElement(Element& element, unsigned offset, unsigned elementsArrayOffset) const
-{
-    setCachedElement(element, offset);
-    if (overridesItemAfter())
-        static_cast<const HTMLCollection*>(this)->m_cachedElementsArrayOffset = elementsArrayOffset;
-
-    ASSERT(overridesItemAfter() || !elementsArrayOffset);
-}
-
 unsigned HTMLCollection::length() const
 {
     if (isLengthCacheValid())
@@ -335,19 +326,18 @@ Node* HTMLCollection::item(unsigned offset) const
 
     ContainerNode& root = rootNode();
 
-    if (isLengthCacheValid() && !overridesItemAfter() && isLastItemCloserThanLastOrCachedItem(offset)) {
+    if (isLengthCacheValid() && !usesCustomForwardOnlyTraversal() && isLastItemCloserThanLastOrCachedItem(offset)) {
         Element* lastItem = itemBefore(0);
         ASSERT(lastItem);
-        setCachedElement(*lastItem, cachedLength() - 1, 0);
-    } else if (!isElementCacheValid() || isFirstItemCloserThanCachedItem(offset) || (overridesItemAfter() && offset < cachedElementOffset())) {
-        unsigned offsetInArray = 0;
-        Element* firstItem = traverseFirstElement(offsetInArray, &root);
+        setCachedElement(*lastItem, cachedLength() - 1);
+    } else if (!isElementCacheValid() || isFirstItemCloserThanCachedItem(offset) || (usesCustomForwardOnlyTraversal() && offset < cachedElementOffset())) {
+        Element* firstItem = firstElement(&root);
 
         if (!firstItem) {
             setLengthCache(0);
             return 0;
         }
-        setCachedElement(*firstItem, 0, offsetInArray);
+        setCachedElement(*firstItem, 0);
         ASSERT(!cachedElementOffset());
     }
 
@@ -365,12 +355,12 @@ inline Element* HTMLCollection::elementBeforeOrAfterCachedElement(unsigned offse
     ASSERT(currentOffset != offset);
 
     if (offset < cachedElementOffset()) {
-        ASSERT(!overridesItemAfter());
+        ASSERT(!usesCustomForwardOnlyTraversal());
         while ((currentItem = itemBefore(currentItem))) {
             ASSERT(currentOffset);
             currentOffset--;
             if (currentOffset == offset) {
-                setCachedElement(*currentItem, currentOffset, 0);
+                setCachedElement(*currentItem, currentOffset);
                 return currentItem;
             }
         }
@@ -378,24 +368,17 @@ inline Element* HTMLCollection::elementBeforeOrAfterCachedElement(unsigned offse
         return 0;
     }
 
-    unsigned offsetInArray = 0;
-    currentItem = traverseForwardToOffset(offset, currentItem, currentOffset, offsetInArray, root);
+    currentItem = traverseForwardToOffset(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, offsetInArray);
+    setCachedElement(*currentItem, currentOffset);
     return currentItem;
 }
 
-Element* HTMLCollection::virtualItemAfter(unsigned&, Element*) const
-{
-    ASSERT_NOT_REACHED();
-    return 0;
-}
-
 static inline bool nameShouldBeVisibleInDocumentAll(HTMLElement* element)
 {
     // The document.all collection returns only certain types of elements by name,
@@ -425,32 +408,20 @@ inline Element* nextMatchingSiblingElement(const HTMLCollection* nodeList, Eleme
     return current;
 }
 
-inline Element* HTMLCollection::traverseFirstElement(unsigned& offsetInArray, ContainerNode* root) const
-{
-    if (overridesItemAfter())
-        return virtualItemAfter(offsetInArray, 0);
-    ASSERT(!offsetInArray);
-    if (m_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
+inline Element* HTMLCollection::firstElement(ContainerNode* root) const
 {
-    if (overridesItemAfter())
-        return virtualItemAfter(offsetInArray, previous);
-    ASSERT(!offsetInArray);
+    if (usesCustomForwardOnlyTraversal())
+        return customElementAfter(nullptr);
     if (m_shouldOnlyIncludeDirectChildren)
-        return nextMatchingSiblingElement(this, previous);
-    return nextMatchingElement(this, previous, root);
+        return firstMatchingChildElement(this, root);
+    return firstMatchingElement(this, root);
 }
 
-inline Element* HTMLCollection::traverseForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, unsigned& offsetInArray, ContainerNode* root) const
+inline Element* HTMLCollection::traverseForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root) const
 {
     ASSERT_WITH_SECURITY_IMPLICATION(currentOffset < offset);
-    if (overridesItemAfter()) {
-        offsetInArray = m_cachedElementsArrayOffset;
-        while ((currentElement = virtualItemAfter(offsetInArray, currentElement))) {
+    if (usesCustomForwardOnlyTraversal()) {
+        while ((currentElement = customElementAfter(currentElement))) {
             if (++currentOffset == offset)
                 return currentElement;
         }
@@ -475,7 +446,6 @@ void HTMLCollection::invalidateCache() const
     m_isItemRefElementsCacheValid = false;
     m_idCache.clear();
     m_nameCache.clear();
-    m_cachedElementsArrayOffset = 0;
 }
 
 void HTMLCollection::invalidateIdNameCacheMaps() const
@@ -496,7 +466,7 @@ Node* HTMLCollection::namedItem(const AtomicString& name) const
         return 0;
 
     ContainerNode& root = rootNode();
-    if (!overridesItemAfter() && root.isInTreeScope()) {
+    if (!usesCustomForwardOnlyTraversal() && root.isInTreeScope()) {
         TreeScope& treeScope = root.treeScope();
         Element* candidate = 0;
         if (treeScope.hasElementWithId(*name.impl())) {
@@ -540,8 +510,8 @@ void HTMLCollection::updateNameCache() const
 
     ContainerNode& root = rootNode();
 
-    unsigned arrayOffset = 0;
-    for (Element* element = traverseFirstElement(arrayOffset, &root); element; element = traverseNextElement(arrayOffset, element, &root)) {
+    unsigned offset = 0;
+    for (Element* element = firstElement(&root); element; element = traverseForwardToOffset(offset + 1, element, offset, &root)) {
         const AtomicString& idAttrVal = element->getIdAttribute();
         if (!idAttrVal.isEmpty())
             appendIdCache(idAttrVal, element);
index c749b49..766c102 100644 (file)
@@ -39,11 +39,6 @@ class Element;
 
 class HTMLCollection : public ScriptWrappable, public RefCounted<HTMLCollection> {
 public:
-    enum ItemAfterOverrideType {
-        OverridesItemAfter,
-        DoesNotOverrideItemAfter,
-    };
-
     static PassRefPtr<HTMLCollection> create(ContainerNode& base, CollectionType);
     virtual ~HTMLCollection();
 
@@ -54,7 +49,7 @@ public:
     PassRefPtr<NodeList> tags(const String&);
 
     // Non-DOM API
-    virtual bool hasNamedItem(const AtomicString& name) const;
+    bool hasNamedItem(const AtomicString& name) const;
     void namedItems(const AtomicString& name, Vector<Ref<Element>>&) const;
     bool isEmpty() const
     {
@@ -73,27 +68,26 @@ public:
         return item(0) && !item(1);
     }
 
-    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;
+    Element* firstElement(ContainerNode* root) const;
+    Element* traverseForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, 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); }
+    bool isRootedAtDocument() const { return m_rootType == NodeListIsRootedAtDocument; }
+    NodeListInvalidationType invalidationType() const { return static_cast<NodeListInvalidationType>(m_invalidationType); }
+    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
+    void invalidateCache(const QualifiedName* attrName) const
     {
         if (!attrName || shouldInvalidateTypeOnAttributeChange(invalidationType(), *attrName))
             invalidateCache();
         else if (*attrName == HTMLNames::idAttr || *attrName == HTMLNames::nameAttr)
             invalidateIdNameCacheMaps();
     }
-    void invalidateCache() const;
+    virtual void invalidateCache() const;
     void invalidateIdNameCacheMaps() const;
 
 protected:
-    HTMLCollection(ContainerNode& base, CollectionType, ItemAfterOverrideType);
+    enum ElementTraversalType { NormalTraversal, CustomForwardOnlyTraversal };
+    HTMLCollection(ContainerNode& base, CollectionType, ElementTraversalType = NormalTraversal);
 
     virtual void updateNameCache() const;
 
@@ -105,38 +99,35 @@ protected:
 
     Document& document() const { return m_ownerNode->document(); }
     ContainerNode& rootNode() const;
-    bool overridesItemAfter() const { return m_overridesItemAfter; }
+    bool usesCustomForwardOnlyTraversal() const { return m_usesCustomForwardOnlyTraversal; }
 
-    ALWAYS_INLINE bool isElementCacheValid() const { return m_isElementCacheValid; }
-    ALWAYS_INLINE Element* cachedElement() const { return m_cachedElement; }
-    ALWAYS_INLINE unsigned cachedElementOffset() const { return m_cachedElementOffset; }
+    bool isElementCacheValid() const { return m_isElementCacheValid; }
+    Element* cachedElement() const { return m_cachedElement; }
+    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
+    bool isLengthCacheValid() const { return m_isLengthCacheValid; }
+    unsigned cachedLength() const { return m_cachedLength; }
+    void setLengthCache(unsigned length) const
     {
         m_cachedLength = length;
         m_isLengthCacheValid = true;
     }
-    ALWAYS_INLINE void setCachedElement(Element& element, unsigned offset) const
+    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; }
+    bool isItemRefElementsCacheValid() const { return m_isItemRefElementsCacheValid; }
+    void setItemRefElementsCacheValid() const { m_isItemRefElementsCacheValid = true; }
 
-    ALWAYS_INLINE NodeListRootType rootType() const { return static_cast<NodeListRootType>(m_rootType); }
+    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;
-
     static void append(NodeCacheMap&, const AtomicString&, Element*);
 
     Element* elementBeforeOrAfterCachedElement(unsigned offset, ContainerNode* root) const;
@@ -145,6 +136,8 @@ private:
     Element* iterateForPreviousElement(Element* current) const;
     Element* itemBefore(Element* previousItem) const;
 
+    virtual Element* customElementAfter(Element*) const { ASSERT_NOT_REACHED(); return nullptr; }
+
     Ref<ContainerNode> m_ownerNode;
     mutable Element* m_cachedElement;
     mutable unsigned m_cachedLength;
@@ -155,15 +148,13 @@ private:
     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;
+    const unsigned m_usesCustomForwardOnlyTraversal : 1;
     mutable unsigned m_isItemRefElementsCacheValid : 1;
 
     mutable NodeCacheMap m_idCache;
     mutable NodeCacheMap m_nameCache;
-    mutable unsigned m_cachedElementsArrayOffset;
 };
 
 } // namespace
index 3c57d6f..5458730 100644 (file)
@@ -36,7 +36,9 @@ using namespace HTMLNames;
 // calculation every time if anything has changed.
 
 HTMLFormControlsCollection::HTMLFormControlsCollection(ContainerNode& ownerNode)
-    : HTMLCollection(ownerNode, FormControls, OverridesItemAfter)
+    : HTMLCollection(ownerNode, FormControls, CustomForwardOnlyTraversal)
+    , m_cachedElement(nullptr)
+    , m_cachedElementOffsetInArray(0)
 {
     ASSERT(isHTMLFormElement(ownerNode) || isHTMLFieldSetElement(ownerNode));
 }
@@ -64,16 +66,34 @@ const Vector<HTMLImageElement*>& HTMLFormControlsCollection::formImageElements()
     return toHTMLFormElement(ownerNode()).imageElements();
 }
 
-Element* HTMLFormControlsCollection::virtualItemAfter(unsigned& offset, Element* previousItem) const
+static unsigned findFormAssociatedElement(const Vector<FormAssociatedElement*>& elements, const Element& element)
 {
-    const Vector<FormAssociatedElement*>& elementsArray = formControlElements();
-    if (previousItem)
-        offset++;
-    while (offset < elementsArray.size()) {
-        FormAssociatedElement& element = *elementsArray[offset];
-        if (element.isEnumeratable())
+    for (unsigned i = 0; i < elements.size(); ++i) {
+        auto& associatedElement = *elements[i];
+        if (associatedElement.isEnumeratable() && &associatedElement.asHTMLElement() == &element)
+            return i;
+    }
+    return elements.size();
+}
+
+Element* HTMLFormControlsCollection::customElementAfter(Element* current) const
+{
+    const Vector<FormAssociatedElement*>& elements = formControlElements();
+    unsigned start;
+    if (!current)
+        start = 0;
+    else if (m_cachedElement == current)
+        start = m_cachedElementOffsetInArray + 1;
+    else
+        start = findFormAssociatedElement(elements, *current) + 1;
+
+    for (unsigned i = start; i < elements.size(); ++i) {
+        FormAssociatedElement& element = *elements[i];
+        if (element.isEnumeratable()) {
+            m_cachedElement = &element.asHTMLElement();
+            m_cachedElementOffsetInArray = i;
             return &element.asHTMLElement();
-        offset++;
+        }
     }
     return nullptr;
 }
@@ -157,4 +177,11 @@ void HTMLFormControlsCollection::updateNameCache() const
     setHasNameCache();
 }
 
+void HTMLFormControlsCollection::invalidateCache() const
+{
+    HTMLCollection::invalidateCache();
+    m_cachedElement = nullptr;
+    m_cachedElementOffsetInArray = 0;
+}
+
 }
index e4372ee..54450d7 100644 (file)
@@ -46,11 +46,15 @@ public:
 private:
     explicit HTMLFormControlsCollection(ContainerNode&);
 
-    virtual void updateNameCache() const;
+    virtual void invalidateCache() const OVERRIDE;
+    virtual void updateNameCache() const OVERRIDE;
 
     const Vector<FormAssociatedElement*>& formControlElements() const;
     const Vector<HTMLImageElement*>& formImageElements() const;
-    virtual Element* virtualItemAfter(unsigned& offsetInArray, Element*) const OVERRIDE;
+    virtual Element* customElementAfter(Element*) const OVERRIDE;
+
+    mutable Element* m_cachedElement;
+    mutable unsigned m_cachedElementOffsetInArray;
 };
 
 } // namespace
index ef09f0b..50eda48 100644 (file)
@@ -37,7 +37,7 @@ namespace WebCore {
 using namespace HTMLNames;
 
 HTMLNameCollection::HTMLNameCollection(Document& document, CollectionType type, const AtomicString& name)
-    : HTMLCollection(document, type, DoesNotOverrideItemAfter)
+    : HTMLCollection(document, type)
     , m_name(name)
 {
 }
index 2871ca5..c8f1231 100644 (file)
@@ -27,7 +27,7 @@
 namespace WebCore {
 
 HTMLOptionsCollection::HTMLOptionsCollection(HTMLSelectElement& select)
-    : HTMLCollection(select, SelectOptions, DoesNotOverrideItemAfter)
+    : HTMLCollection(select, SelectOptions)
 {
 }
 
index 52c3093..7557b0e 100644 (file)
@@ -148,7 +148,7 @@ HTMLTableRowElement* HTMLTableRowsCollection::lastRow(HTMLTableElement* table)
 }
 
 HTMLTableRowsCollection::HTMLTableRowsCollection(HTMLTableElement& table)
-    : HTMLCollection(table, TableRows, OverridesItemAfter)
+    : HTMLCollection(table, TableRows, CustomForwardOnlyTraversal)
 {
 }
 
@@ -158,9 +158,8 @@ PassRef<HTMLTableRowsCollection> HTMLTableRowsCollection::create(HTMLTableElemen
     return adoptRef(*new HTMLTableRowsCollection(table));
 }
 
-Element* HTMLTableRowsCollection::virtualItemAfter(unsigned& offsetInArray, Element* previous) const
+Element* HTMLTableRowsCollection::customElementAfter(Element* previous) const
 {
-    ASSERT_UNUSED(offsetInArray, !offsetInArray);
     return rowAfter(const_cast<HTMLTableElement*>(&tableElement()), toHTMLTableRowElement(previous));
 }
 
index c445df2..e011f5b 100644 (file)
@@ -49,7 +49,7 @@ public:
 private:
     explicit HTMLTableRowsCollection(HTMLTableElement&);
 
-    virtual Element* virtualItemAfter(unsigned& offsetInArray, Element*) const OVERRIDE;
+    virtual Element* customElementAfter(Element*) const OVERRIDE;
 };
 
 } // namespace