Changeset 158774 in webkit


Ignore:
Timestamp:
Nov 6, 2013 12:38:37 PM (10 years ago)
Author:
Antti Koivisto
Message:

HTMLCollection should use CollectionIndexCache
https://bugs.webkit.org/show_bug.cgi?id=123906

Reviewed by Ryosuke Niwa.

More code sharing.

  • bindings/js/JSDOMWindowCustom.cpp:

(WebCore::namedItemGetter):

  • bindings/js/JSHTMLDocumentCustom.cpp:

(WebCore::JSHTMLDocument::nameGetter):

  • dom/ChildNodeList.h:
  • dom/CollectionIndexCache.h:

(WebCore::::nodeBeforeCached):
(WebCore::::nodeAfterCached):
(WebCore::::nodeAt):

Add a mechanism for disabling use of backward traversal.

  • dom/LiveNodeList.h:

(WebCore::LiveNodeList::collectionCanTraverseBackward):

  • html/HTMLCollection.cpp:

(WebCore::HTMLCollection::HTMLCollection):
(WebCore::isMatchingElement):
(WebCore::HTMLCollection::iterateForPreviousElement):
(WebCore::firstMatchingElement):
(WebCore::nextMatchingElement):
(WebCore::HTMLCollection::length):
(WebCore::HTMLCollection::item):
(WebCore::nameShouldBeVisibleInDocumentAll):
(WebCore::firstMatchingChildElement):
(WebCore::nextMatchingSiblingElement):
(WebCore::HTMLCollection::firstElement):
(WebCore::HTMLCollection::traverseForward):
(WebCore::HTMLCollection::collectionFirst):
(WebCore::HTMLCollection::collectionLast):
(WebCore::HTMLCollection::collectionTraverseForward):
(WebCore::HTMLCollection::collectionTraverseBackward):
(WebCore::HTMLCollection::invalidateCache):
(WebCore::HTMLCollection::namedItem):
(WebCore::HTMLCollection::updateNameCache):

  • html/HTMLCollection.h:

(WebCore::HTMLCollection::collectionCanTraverseBackward):

Disable use of backward traversal for collections that use custom traversal.

Location:
trunk/Source/WebCore
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r158760 r158774  
     12013-11-06  Antti Koivisto  <antti@apple.com>
     2
     3        HTMLCollection should use CollectionIndexCache
     4        https://bugs.webkit.org/show_bug.cgi?id=123906
     5
     6        Reviewed by Ryosuke Niwa.
     7       
     8        More code sharing.
     9
     10        * bindings/js/JSDOMWindowCustom.cpp:
     11        (WebCore::namedItemGetter):
     12        * bindings/js/JSHTMLDocumentCustom.cpp:
     13        (WebCore::JSHTMLDocument::nameGetter):
     14        * dom/ChildNodeList.h:
     15        * dom/CollectionIndexCache.h:
     16        (WebCore::::nodeBeforeCached):
     17        (WebCore::::nodeAfterCached):
     18        (WebCore::::nodeAt):
     19           
     20            Add a mechanism for disabling use of backward traversal.
     21
     22        * dom/LiveNodeList.h:
     23        (WebCore::LiveNodeList::collectionCanTraverseBackward):
     24        * html/HTMLCollection.cpp:
     25        (WebCore::HTMLCollection::HTMLCollection):
     26        (WebCore::isMatchingElement):
     27        (WebCore::HTMLCollection::iterateForPreviousElement):
     28        (WebCore::firstMatchingElement):
     29        (WebCore::nextMatchingElement):
     30        (WebCore::HTMLCollection::length):
     31        (WebCore::HTMLCollection::item):
     32        (WebCore::nameShouldBeVisibleInDocumentAll):
     33        (WebCore::firstMatchingChildElement):
     34        (WebCore::nextMatchingSiblingElement):
     35        (WebCore::HTMLCollection::firstElement):
     36        (WebCore::HTMLCollection::traverseForward):
     37        (WebCore::HTMLCollection::collectionFirst):
     38        (WebCore::HTMLCollection::collectionLast):
     39        (WebCore::HTMLCollection::collectionTraverseForward):
     40        (WebCore::HTMLCollection::collectionTraverseBackward):
     41        (WebCore::HTMLCollection::invalidateCache):
     42        (WebCore::HTMLCollection::namedItem):
     43        (WebCore::HTMLCollection::updateNameCache):
     44        * html/HTMLCollection.h:
     45        (WebCore::HTMLCollection::collectionCanTraverseBackward):
     46       
     47            Disable use of backward traversal for collections that use custom traversal.
     48
    1492013-11-06  Brendan Long  <b.long@cablelabs.com>
    250
  • trunk/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp

    r157228 r158774  
    102102    if (UNLIKELY(toHTMLDocument(document)->windowNamedItemContainsMultipleElements(*atomicPropertyName))) {
    103103        RefPtr<HTMLCollection> collection = document->windowNamedItems(atomicPropertyName);
    104         ASSERT(!collection->isEmpty());
    105         ASSERT(!collection->hasExactlyOneItem());
     104        ASSERT(collection->length() > 1);
    106105        return toJS(exec, thisObj->globalObject(), WTF::getPtr(collection));
    107106    }
  • trunk/Source/WebCore/bindings/js/JSHTMLDocumentCustom.cpp

    r157215 r158774  
    6969    if (UNLIKELY(document.documentNamedItemContainsMultipleElements(*atomicPropertyName))) {
    7070        RefPtr<HTMLCollection> collection = document.documentNamedItems(atomicPropertyName);
    71         ASSERT(!collection->isEmpty());
    72         ASSERT(!collection->hasExactlyOneItem());
     71        ASSERT(collection->length() > 1);
    7372        return toJS(exec, thisObj->globalObject(), WTF::getPtr(collection));
    7473    }
  • trunk/Source/WebCore/dom/ChildNodeList.h

    r158698 r158774  
    7474    Node* collectionTraverseForward(Node&, unsigned count, unsigned& traversedCount) const;
    7575    Node* collectionTraverseBackward(Node&, unsigned count) const;
     76    bool collectionCanTraverseBackward() const { return true; }
    7677
    7778private:
  • trunk/Source/WebCore/dom/CollectionIndexCache.h

    r158698 r158774  
    8181
    8282    bool firstIsCloser = index < m_currentIndex - index;
    83     if (firstIsCloser) {
     83    if (firstIsCloser || !collection.collectionCanTraverseBackward()) {
    8484        m_currentNode = collection.collectionFirst();
    8585        m_currentIndex = 0;
     
    105105
    106106    bool lastIsCloser = m_nodeCountValid && m_nodeCount - index < index - m_currentIndex;
    107     if (lastIsCloser) {
     107    if (lastIsCloser && collection.collectionCanTraverseBackward()) {
    108108        m_currentNode = collection.collectionLast();
    109109        if (index < m_nodeCount - 1)
     
    143143
    144144    bool lastIsCloser = m_nodeCountValid && m_nodeCount - index < index;
    145     if (lastIsCloser) {
     145    if (lastIsCloser && collection.collectionCanTraverseBackward()) {
    146146        m_currentNode = collection.collectionLast();
    147147        if (index < m_nodeCount - 1)
  • trunk/Source/WebCore/dom/LiveNodeList.h

    r158698 r158774  
    6767        document().registerNodeList(*this);
    6868    }
    69     virtual Node* namedItem(const AtomicString&) const OVERRIDE;
     69    virtual Node* namedItem(const AtomicString&) const OVERRIDE FINAL;
    7070    virtual bool nodeMatches(Element*) const = 0;
    7171
     
    7676
    7777    // DOM API
    78     virtual unsigned length() const OVERRIDE;
    79     virtual Node* item(unsigned offset) const OVERRIDE;
     78    virtual unsigned length() const OVERRIDE FINAL;
     79    virtual Node* item(unsigned offset) const OVERRIDE FINAL;
    8080
    8181    ALWAYS_INLINE bool isRootedAtDocument() const { return m_rootType == NodeListIsRootedAtDocument; }
     
    9595    Element* collectionTraverseForward(Element&, unsigned count, unsigned& traversedCount) const;
    9696    Element* collectionTraverseBackward(Element&, unsigned count) const;
     97    bool collectionCanTraverseBackward() const { return true; }
    9798
    9899protected:
  • trunk/Source/WebCore/html/HTMLCollection.cpp

    r158758 r158774  
    134134HTMLCollection::HTMLCollection(ContainerNode& ownerNode, CollectionType type, ElementTraversalType traversalType)
    135135    : m_ownerNode(ownerNode)
    136     , m_cachedElement(nullptr)
    137     , m_isLengthCacheValid(false)
    138     , m_isElementCacheValid(false)
    139136    , m_rootType(rootTypeFromCollectionType(type))
    140137    , m_invalidationType(invalidationTypeExcludingIdAndNameAttributes(type))
     
    173170}
    174171
    175 inline bool isMatchingElement(const HTMLCollection* htmlCollection, Element* element)
    176 {
    177     CollectionType type = htmlCollection->type();
    178     if (!element->isHTMLElement() && !(type == DocAll || type == NodeChildren || type == WindowNamedItems))
     172inline bool isMatchingElement(const HTMLCollection& htmlCollection, Element& element)
     173{
     174    CollectionType type = htmlCollection.type();
     175    if (!element.isHTMLElement() && !(type == DocAll || type == NodeChildren || type == WindowNamedItems))
    179176        return false;
    180177
    181178    switch (type) {
    182179    case DocImages:
    183         return element->hasLocalName(imgTag);
     180        return element.hasLocalName(imgTag);
    184181    case DocScripts:
    185         return element->hasLocalName(scriptTag);
     182        return element.hasLocalName(scriptTag);
    186183    case DocForms:
    187         return element->hasLocalName(formTag);
     184        return element.hasLocalName(formTag);
    188185    case TableTBodies:
    189         return element->hasLocalName(tbodyTag);
     186        return element.hasLocalName(tbodyTag);
    190187    case TRCells:
    191         return element->hasLocalName(tdTag) || element->hasLocalName(thTag);
     188        return element.hasLocalName(tdTag) || element.hasLocalName(thTag);
    192189    case TSectionRows:
    193         return element->hasLocalName(trTag);
     190        return element.hasLocalName(trTag);
    194191    case SelectOptions:
    195         return element->hasLocalName(optionTag);
     192        return element.hasLocalName(optionTag);
    196193    case SelectedOptions:
    197         return element->hasLocalName(optionTag) && toHTMLOptionElement(element)->selected();
     194        return element.hasLocalName(optionTag) && toHTMLOptionElement(element).selected();
    198195    case DataListOptions:
    199         if (element->hasLocalName(optionTag)) {
    200             HTMLOptionElement* option = toHTMLOptionElement(element);
    201             if (!option->isDisabledFormControl() && !option->value().isEmpty())
     196        if (element.hasLocalName(optionTag)) {
     197            HTMLOptionElement& option = toHTMLOptionElement(element);
     198            if (!option.isDisabledFormControl() && !option.value().isEmpty())
    202199                return true;
    203200        }
    204201        return false;
    205202    case MapAreas:
    206         return element->hasLocalName(areaTag);
     203        return element.hasLocalName(areaTag);
    207204    case DocApplets:
    208         return element->hasLocalName(appletTag) || (element->hasLocalName(objectTag) && static_cast<HTMLObjectElement*>(element)->containsJavaApplet());
     205        return element.hasLocalName(appletTag) || (element.hasLocalName(objectTag) && toHTMLObjectElement(element).containsJavaApplet());
    209206    case DocEmbeds:
    210         return element->hasLocalName(embedTag);
     207        return element.hasLocalName(embedTag);
    211208    case DocLinks:
    212         return (element->hasLocalName(aTag) || element->hasLocalName(areaTag)) && element->fastHasAttribute(hrefAttr);
     209        return (element.hasLocalName(aTag) || element.hasLocalName(areaTag)) && element.fastHasAttribute(hrefAttr);
    213210    case DocAnchors:
    214         return element->hasLocalName(aTag) && element->fastHasAttribute(nameAttr);
     211        return element.hasLocalName(aTag) && element.fastHasAttribute(nameAttr);
    215212    case DocAll:
    216213    case NodeChildren:
    217214        return true;
    218215    case DocumentNamedItems:
    219         return static_cast<const DocumentNameCollection*>(htmlCollection)->nodeMatches(element);
     216        return static_cast<const DocumentNameCollection&>(htmlCollection).nodeMatches(&element);
    220217    case WindowNamedItems:
    221         return static_cast<const WindowNameCollection*>(htmlCollection)->nodeMatches(element);
     218        return static_cast<const WindowNameCollection&>(htmlCollection).nodeMatches(&element);
    222219    case FormControls:
    223220    case TableRows:
     
    233230}
    234231
    235 static Element* lastElement(ContainerNode& rootNode, bool onlyIncludeDirectChildren)
    236 {
    237     return onlyIncludeDirectChildren ? ElementTraversal::lastChild(&rootNode) : ElementTraversal::lastWithin(&rootNode);
    238 }
    239 
    240232ALWAYS_INLINE Element* HTMLCollection::iterateForPreviousElement(Element* current) const
    241233{
     
    243235    ContainerNode& rootNode = this->rootNode();
    244236    for (; current; current = previousElement(rootNode, current, onlyIncludeDirectChildren)) {
    245         if (isMatchingElement(this, current))
     237        if (isMatchingElement(*this, *current))
    246238            return current;
    247239    }
    248     return 0;
    249 }
    250 
    251 ALWAYS_INLINE Element* HTMLCollection::itemBefore(Element* previous) const
    252 {
    253     Element* current;
    254     if (LIKELY(!!previous)) // Without this LIKELY, length() and item() can be 10% slower.
    255         current = previousElement(rootNode(), previous, m_shouldOnlyIncludeDirectChildren);
    256     else
    257         current = lastElement(rootNode(), m_shouldOnlyIncludeDirectChildren);
    258 
    259     return iterateForPreviousElement(current);
    260 }
    261 
    262 inline Element* firstMatchingElement(const HTMLCollection* collection, ContainerNode* root)
    263 {
    264     Element* element = ElementTraversal::firstWithin(root);
    265     while (element && !isMatchingElement(collection, element))
    266         element = ElementTraversal::next(element, root);
     240    return nullptr;
     241}
     242
     243inline Element* firstMatchingElement(const HTMLCollection& collection, ContainerNode& root)
     244{
     245    Element* element = ElementTraversal::firstWithin(&root);
     246    while (element && !isMatchingElement(collection, *element))
     247        element = ElementTraversal::next(element, &root);
    267248    return element;
    268249}
    269250
    270 inline Element* nextMatchingElement(const HTMLCollection* collection, Element* current, ContainerNode* root)
     251inline Element* nextMatchingElement(const HTMLCollection& collection, Element* current, ContainerNode& root)
    271252{
    272253    do {
    273         current = ElementTraversal::next(current, root);
    274     } while (current && !isMatchingElement(collection, current));
     254        current = ElementTraversal::next(current, &root);
     255    } while (current && !isMatchingElement(collection, *current));
    275256    return current;
    276257}
    277258
    278 inline Element* traverseMatchingElementsForwardToOffset(const HTMLCollection* collection, unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root)
    279 {
    280     ASSERT_WITH_SECURITY_IMPLICATION(currentOffset < offset);
    281     while ((currentElement = nextMatchingElement(collection, currentElement, root))) {
    282         if (++currentOffset == offset)
    283             return currentElement;
    284     }
    285     return 0;
    286 }
    287 
    288 bool ALWAYS_INLINE HTMLCollection::isLastItemCloserThanLastOrCachedItem(unsigned offset) const
    289 {
    290     ASSERT(isLengthCacheValid());
    291     unsigned distanceFromLastItem = cachedLength() - offset;
    292     if (!isElementCacheValid())
    293         return distanceFromLastItem < offset;
    294 
    295     return cachedElementOffset() < offset && distanceFromLastItem < offset - cachedElementOffset();
    296 }
    297    
    298 bool ALWAYS_INLINE HTMLCollection::isFirstItemCloserThanCachedItem(unsigned offset) const
    299 {
    300     ASSERT(isElementCacheValid());
    301     if (cachedElementOffset() < offset)
    302         return false;
    303 
    304     unsigned distanceFromCachedItem = cachedElementOffset() - offset;
    305     return offset < distanceFromCachedItem;
    306 }
    307 
    308259unsigned HTMLCollection::length() const
    309260{
    310     if (isLengthCacheValid())
    311         return cachedLength();
    312 
    313     item(UINT_MAX);
    314     ASSERT(isLengthCacheValid());
    315    
    316     return cachedLength();
     261    return m_indexCache.nodeCount(*this);
    317262}
    318263
    319264Node* HTMLCollection::item(unsigned offset) const
    320265{
    321     if (isElementCacheValid() && cachedElementOffset() == offset)
    322         return cachedElement();
    323 
    324     if (isLengthCacheValid() && cachedLength() <= offset)
    325         return 0;
    326 
    327     ContainerNode& root = rootNode();
    328 
    329     if (isLengthCacheValid() && !usesCustomForwardOnlyTraversal() && isLastItemCloserThanLastOrCachedItem(offset)) {
    330         Element* lastItem = itemBefore(0);
    331         ASSERT(lastItem);
    332         setCachedElement(*lastItem, cachedLength() - 1);
    333     } else if (!isElementCacheValid() || isFirstItemCloserThanCachedItem(offset) || (usesCustomForwardOnlyTraversal() && offset < cachedElementOffset())) {
    334         Element* firstItem = firstElement(&root);
    335 
    336         if (!firstItem) {
    337             setLengthCache(0);
    338             return 0;
    339         }
    340         setCachedElement(*firstItem, 0);
    341         ASSERT(!cachedElementOffset());
    342     }
    343 
    344     if (cachedElementOffset() == offset)
    345         return cachedElement();
    346 
    347     return elementBeforeOrAfterCachedElement(offset, &root);
    348 }
    349 
    350 inline Element* HTMLCollection::elementBeforeOrAfterCachedElement(unsigned offset, ContainerNode* root) const
    351 {
    352     unsigned currentOffset = cachedElementOffset();
    353     Element* currentItem = cachedElement();
    354     ASSERT(currentItem);
    355     ASSERT(currentOffset != offset);
    356 
    357     if (offset < cachedElementOffset()) {
    358         ASSERT(!usesCustomForwardOnlyTraversal());
    359         while ((currentItem = itemBefore(currentItem))) {
    360             ASSERT(currentOffset);
    361             currentOffset--;
    362             if (currentOffset == offset) {
    363                 setCachedElement(*currentItem, currentOffset);
    364                 return currentItem;
    365             }
    366         }
    367         ASSERT_NOT_REACHED();
    368         return 0;
    369     }
    370 
    371     currentItem = traverseForwardToOffset(offset, currentItem, currentOffset, root);
    372 
    373     if (!currentItem) {
    374         // Did not find the item. On plus side, we now know the length.
    375         setLengthCache(currentOffset + 1);
    376         return 0;
    377     }
    378     setCachedElement(*currentItem, currentOffset);
    379     return currentItem;
    380 }
    381 
    382 static inline bool nameShouldBeVisibleInDocumentAll(HTMLElement* element)
     266    return m_indexCache.nodeAt(*this, offset);
     267}
     268
     269static inline bool nameShouldBeVisibleInDocumentAll(HTMLElement& element)
    383270{
    384271    // The document.all collection returns only certain types of elements by name,
    385272    // although it returns any type of element by id.
    386     return element->hasLocalName(appletTag)
    387         || element->hasLocalName(embedTag)
    388         || element->hasLocalName(formTag)
    389         || element->hasLocalName(imgTag)
    390         || element->hasLocalName(inputTag)
    391         || element->hasLocalName(objectTag)
    392         || element->hasLocalName(selectTag);
    393 }
    394 
    395 inline Element* firstMatchingChildElement(const HTMLCollection* nodeList, ContainerNode* root)
    396 {
    397     Element* element = ElementTraversal::firstWithin(root);
    398     while (element && !isMatchingElement(nodeList, element))
     273    return element.hasLocalName(appletTag)
     274        || element.hasLocalName(embedTag)
     275        || element.hasLocalName(formTag)
     276        || element.hasLocalName(imgTag)
     277        || element.hasLocalName(inputTag)
     278        || element.hasLocalName(objectTag)
     279        || element.hasLocalName(selectTag);
     280}
     281
     282inline Element* firstMatchingChildElement(const HTMLCollection& nodeList, ContainerNode& root)
     283{
     284    Element* element = ElementTraversal::firstWithin(&root);
     285    while (element && !isMatchingElement(nodeList, *element))
    399286        element = ElementTraversal::nextSibling(element);
    400287    return element;
    401288}
    402289
    403 inline Element* nextMatchingSiblingElement(const HTMLCollection* nodeList, Element* current)
     290inline Element* nextMatchingSiblingElement(const HTMLCollection& nodeList, Element* current)
    404291{
    405292    do {
    406293        current = ElementTraversal::nextSibling(current);
    407     } while (current && !isMatchingElement(nodeList, current));
     294    } while (current && !isMatchingElement(nodeList, *current));
    408295    return current;
    409296}
    410297
    411 inline Element* HTMLCollection::firstElement(ContainerNode* root) const
     298inline Element* HTMLCollection::firstElement(ContainerNode& root) const
    412299{
    413300    if (usesCustomForwardOnlyTraversal())
    414301        return customElementAfter(nullptr);
    415302    if (m_shouldOnlyIncludeDirectChildren)
    416         return firstMatchingChildElement(this, root);
    417     return firstMatchingElement(this, root);
    418 }
    419 
    420 inline Element* HTMLCollection::traverseForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root) const
    421 {
    422     ASSERT_WITH_SECURITY_IMPLICATION(currentOffset < offset);
     303        return firstMatchingChildElement(*this, root);
     304    return firstMatchingElement(*this, root);
     305}
     306
     307inline Element* HTMLCollection::traverseForward(Element& current, unsigned count, unsigned& traversedCount, ContainerNode& root) const
     308{
     309    Element* element = &current;
    423310    if (usesCustomForwardOnlyTraversal()) {
    424         while ((currentElement = customElementAfter(currentElement))) {
    425             if (++currentOffset == offset)
    426                 return currentElement;
     311        for (traversedCount = 0; traversedCount < count; ++traversedCount) {
     312            element = customElementAfter(element);
     313            if (!element)
     314                return nullptr;
    427315        }
    428         return 0;
     316        return element;
    429317    }
    430318    if (m_shouldOnlyIncludeDirectChildren) {
    431         while ((currentElement = nextMatchingSiblingElement(this, currentElement))) {
    432             if (++currentOffset == offset)
    433                 return currentElement;
     319        for (traversedCount = 0; traversedCount < count; ++traversedCount) {
     320            element = nextMatchingSiblingElement(*this, element);
     321            if (!element)
     322                return nullptr;
    434323        }
    435         return 0;
    436     }
    437     return traverseMatchingElementsForwardToOffset(this, offset, currentElement, currentOffset, root);
     324        return element;
     325    }
     326    for (traversedCount = 0; traversedCount < count; ++traversedCount) {
     327        element = nextMatchingElement(*this, element, root);
     328        if (!element)
     329            return nullptr;
     330    }
     331    return element;
     332}
     333
     334Element* HTMLCollection::collectionFirst() const
     335{
     336    return firstElement(rootNode());
     337}
     338
     339Element* HTMLCollection::collectionLast() const
     340{
     341    // FIXME: This should be optimized similarly to the forward case.
     342    auto& root = rootNode();
     343    Element* last = m_shouldOnlyIncludeDirectChildren ? ElementTraversal::lastChild(&root) : ElementTraversal::lastWithin(&root);
     344    return iterateForPreviousElement(last);
     345}
     346
     347Element* HTMLCollection::collectionTraverseForward(Element& current, unsigned count, unsigned& traversedCount) const
     348{
     349    return traverseForward(current, count, traversedCount, rootNode());
     350}
     351
     352Element* HTMLCollection::collectionTraverseBackward(Element& current, unsigned count) const
     353{
     354    // FIXME: This should be optimized similarly to the forward case.
     355    auto& root = rootNode();
     356    Element* element = &current;
     357    for (; count && element ; --count)
     358        element = iterateForPreviousElement(ElementTraversal::previous(element, &root));
     359    return element;
    438360}
    439361
    440362void HTMLCollection::invalidateCache() const
    441363{
    442     m_cachedElement = nullptr;
    443     m_isLengthCacheValid = false;
    444     m_isElementCacheValid = false;
     364    m_indexCache.invalidate();
    445365    m_isNameCacheValid = false;
    446366    m_isItemRefElementsCacheValid = false;
     
    476396            if (!treeScope.containsMultipleElementsWithName(name)) {
    477397                candidate = treeScope.getElementByName(name);
    478                 if (candidate && type() == DocAll && (!candidate->isHTMLElement() || !nameShouldBeVisibleInDocumentAll(toHTMLElement(candidate))))
     398                if (candidate && type() == DocAll && (!candidate->isHTMLElement() || !nameShouldBeVisibleInDocumentAll(toHTMLElement(*candidate))))
    479399                    candidate = 0;
    480400            }
     
    482402            return 0;
    483403
    484         if (candidate
    485             && isMatchingElement(this, candidate)
     404        if (candidate && isMatchingElement(*this, *candidate)
    486405            && (m_shouldOnlyIncludeDirectChildren ? candidate->parentNode() == &root : candidate->isDescendantOf(&root)))
    487406            return candidate;
     
    511430    ContainerNode& root = rootNode();
    512431
    513     unsigned offset = 0;
    514     for (Element* element = firstElement(&root); element; element = traverseForwardToOffset(offset + 1, element, offset, &root)) {
     432    unsigned count;
     433    for (Element* element = firstElement(root); element; element = traverseForward(*element, 1, count, root)) {
    515434        const AtomicString& idAttrVal = element->getIdAttribute();
    516435        if (!idAttrVal.isEmpty())
     
    519438            continue;
    520439        const AtomicString& nameAttrVal = element->getNameAttribute();
    521         if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && (type() != DocAll || nameShouldBeVisibleInDocumentAll(toHTMLElement(element))))
     440        if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && (type() != DocAll || nameShouldBeVisibleInDocumentAll(toHTMLElement(*element))))
    522441            appendNameCache(nameAttrVal, element);
    523442    }
  • trunk/Source/WebCore/html/HTMLCollection.h

    r158758 r158774  
    2424#define HTMLCollection_h
    2525
     26#include "CollectionIndexCache.h"
    2627#include "CollectionType.h"
    2728#include "ContainerNode.h"
     
    5253    bool hasNamedItem(const AtomicString& name) const;
    5354    void namedItems(const AtomicString& name, Vector<Ref<Element>>&) const;
    54     bool isEmpty() const
    55     {
    56         if (isLengthCacheValid())
    57             return !cachedLength();
    58         if (isElementCacheValid())
    59             return !cachedElement();
    60         return !item(0);
    61     }
    62     bool hasExactlyOneItem() const
    63     {
    64         if (isLengthCacheValid())
    65             return cachedLength() == 1;
    66         if (isElementCacheValid())
    67             return cachedElement() && !cachedElementOffset() && !item(1);
    68         return item(0) && !item(1);
    69     }
    70 
    71     Element* firstElement(ContainerNode* root) const;
    72     Element* traverseForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root) const;
    7355
    7456    bool isRootedAtDocument() const { return m_rootType == NodeListIsRootedAtDocument; }
     
    8567    virtual void invalidateCache() const;
    8668    void invalidateIdNameCacheMaps() const;
     69
     70    // For CollectionIndexCache
     71    Element* collectionFirst() const;
     72    Element* collectionLast() const;
     73    Element* collectionTraverseForward(Element&, unsigned count, unsigned& traversedCount) const;
     74    Element* collectionTraverseBackward(Element&, unsigned count) const;
     75    bool collectionCanTraverseBackward() const { return !m_usesCustomForwardOnlyTraversal; }
    8776
    8877protected:
     
    10291    bool usesCustomForwardOnlyTraversal() const { return m_usesCustomForwardOnlyTraversal; }
    10392
    104     bool isElementCacheValid() const { return m_isElementCacheValid; }
    105     Element* cachedElement() const { return m_cachedElement; }
    106     unsigned cachedElementOffset() const { return m_cachedElementOffset; }
    107 
    108     bool isLengthCacheValid() const { return m_isLengthCacheValid; }
    109     unsigned cachedLength() const { return m_cachedLength; }
    110     void setLengthCache(unsigned length) const
    111     {
    112         m_cachedLength = length;
    113         m_isLengthCacheValid = true;
    114     }
    115     void setCachedElement(Element& element, unsigned offset) const
    116     {
    117         m_cachedElement = &element;
    118         m_cachedElementOffset = offset;
    119         m_isElementCacheValid = true;
    120     }
    121 
    12293    bool isItemRefElementsCacheValid() const { return m_isItemRefElementsCacheValid; }
    12394    void setItemRefElementsCacheValid() const { m_isItemRefElementsCacheValid = true; }
     
    131102    static void append(NodeCacheMap&, const AtomicString&, Element*);
    132103
    133     Element* elementBeforeOrAfterCachedElement(unsigned offset, ContainerNode* root) const;
    134     bool isLastItemCloserThanLastOrCachedItem(unsigned offset) const;
    135     bool isFirstItemCloserThanCachedItem(unsigned offset) const;
    136104    Element* iterateForPreviousElement(Element* current) const;
    137     Element* itemBefore(Element* previousItem) const;
     105    Element* firstElement(ContainerNode& root) const;
     106    Element* traverseForward(Element& current, unsigned count, unsigned& traversedCount, ContainerNode& root) const;
    138107
    139108    virtual Element* customElementAfter(Element*) const { ASSERT_NOT_REACHED(); return nullptr; }
    140109
    141110    Ref<ContainerNode> m_ownerNode;
    142     mutable Element* m_cachedElement;
    143     mutable unsigned m_cachedLength;
    144     mutable unsigned m_cachedElementOffset;
    145     mutable unsigned m_isLengthCacheValid : 1;
    146     mutable unsigned m_isElementCacheValid : 1;
     111
     112    mutable CollectionIndexCache<HTMLCollection, Element> m_indexCache;
     113
    147114    const unsigned m_rootType : 2;
    148115    const unsigned m_invalidationType : 4;
Note: See TracChangeset for help on using the changeset viewer.