Home | History | Annotate | Download | only in html
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights reserved.
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Library General Public
      8  * License as published by the Free Software Foundation; either
      9  * version 2 of the License, or (at your option) any later version.
     10  *
     11  * This library is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  * Library General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU Library General Public License
     17  * along with this library; see the file COPYING.LIB.  If not, write to
     18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19  * Boston, MA 02110-1301, USA.
     20  *
     21  */
     22 
     23 #include "config.h"
     24 #include "core/html/HTMLCollection.h"
     25 
     26 #include "HTMLNames.h"
     27 #include "core/dom/ClassNodeList.h"
     28 #include "core/dom/NodeList.h"
     29 #include "core/dom/NodeRareData.h"
     30 #include "core/dom/NodeTraversal.h"
     31 #include "core/html/HTMLElement.h"
     32 #include "core/html/HTMLObjectElement.h"
     33 #include "core/html/HTMLOptionElement.h"
     34 
     35 namespace WebCore {
     36 
     37 using namespace HTMLNames;
     38 
     39 static bool shouldOnlyIncludeDirectChildren(CollectionType type)
     40 {
     41     switch (type) {
     42     case DocAll:
     43     case DocAnchors:
     44     case DocApplets:
     45     case DocEmbeds:
     46     case DocForms:
     47     case DocImages:
     48     case DocLinks:
     49     case DocScripts:
     50     case DocumentNamedItems:
     51     case MapAreas:
     52     case TableRows:
     53     case SelectOptions:
     54     case SelectedOptions:
     55     case DataListOptions:
     56     case WindowNamedItems:
     57     case FormControls:
     58         return false;
     59     case NodeChildren:
     60     case TRCells:
     61     case TSectionRows:
     62     case TableTBodies:
     63         return true;
     64     case ChildNodeListType:
     65     case ClassNodeListType:
     66     case NameNodeListType:
     67     case TagNodeListType:
     68     case HTMLTagNodeListType:
     69     case RadioNodeListType:
     70     case LabelsNodeListType:
     71         break;
     72     }
     73     ASSERT_NOT_REACHED();
     74     return false;
     75 }
     76 
     77 static NodeListRootType rootTypeFromCollectionType(CollectionType type)
     78 {
     79     switch (type) {
     80     case DocImages:
     81     case DocApplets:
     82     case DocEmbeds:
     83     case DocForms:
     84     case DocLinks:
     85     case DocAnchors:
     86     case DocScripts:
     87     case DocAll:
     88     case WindowNamedItems:
     89     case DocumentNamedItems:
     90     case FormControls:
     91         return NodeListIsRootedAtDocument;
     92     case NodeChildren:
     93     case TableTBodies:
     94     case TSectionRows:
     95     case TableRows:
     96     case TRCells:
     97     case SelectOptions:
     98     case SelectedOptions:
     99     case DataListOptions:
    100     case MapAreas:
    101         return NodeListIsRootedAtNode;
    102     case ChildNodeListType:
    103     case ClassNodeListType:
    104     case NameNodeListType:
    105     case TagNodeListType:
    106     case HTMLTagNodeListType:
    107     case RadioNodeListType:
    108     case LabelsNodeListType:
    109         break;
    110     }
    111     ASSERT_NOT_REACHED();
    112     return NodeListIsRootedAtNode;
    113 }
    114 
    115 static NodeListInvalidationType invalidationTypeExcludingIdAndNameAttributes(CollectionType type)
    116 {
    117     switch (type) {
    118     case DocImages:
    119     case DocEmbeds:
    120     case DocForms:
    121     case DocScripts:
    122     case DocAll:
    123     case NodeChildren:
    124     case TableTBodies:
    125     case TSectionRows:
    126     case TableRows:
    127     case TRCells:
    128     case SelectOptions:
    129     case MapAreas:
    130         return DoNotInvalidateOnAttributeChanges;
    131     case DocApplets:
    132     case SelectedOptions:
    133     case DataListOptions:
    134         // FIXME: We can do better some day.
    135         return InvalidateOnAnyAttrChange;
    136     case DocAnchors:
    137         return InvalidateOnNameAttrChange;
    138     case DocLinks:
    139         return InvalidateOnHRefAttrChange;
    140     case WindowNamedItems:
    141         return InvalidateOnIdNameAttrChange;
    142     case DocumentNamedItems:
    143         return InvalidateOnIdNameAttrChange;
    144     case FormControls:
    145         return InvalidateForFormControls;
    146     case ChildNodeListType:
    147     case ClassNodeListType:
    148     case NameNodeListType:
    149     case TagNodeListType:
    150     case HTMLTagNodeListType:
    151     case RadioNodeListType:
    152     case LabelsNodeListType:
    153         break;
    154     }
    155     ASSERT_NOT_REACHED();
    156     return DoNotInvalidateOnAttributeChanges;
    157 }
    158 
    159 HTMLCollection::HTMLCollection(Node* ownerNode, CollectionType type, ItemAfterOverrideType itemAfterOverrideType)
    160     : LiveNodeListBase(ownerNode, rootTypeFromCollectionType(type), invalidationTypeExcludingIdAndNameAttributes(type),
    161         WebCore::shouldOnlyIncludeDirectChildren(type), type, itemAfterOverrideType)
    162 {
    163     ScriptWrappable::init(this);
    164 }
    165 
    166 PassRefPtr<HTMLCollection> HTMLCollection::create(Node* base, CollectionType type)
    167 {
    168     return adoptRef(new HTMLCollection(base, type, DoesNotOverrideItemAfter));
    169 }
    170 
    171 HTMLCollection::~HTMLCollection()
    172 {
    173     // HTMLNameCollection removes cache by itself.
    174     if (type() != WindowNamedItems && type() != DocumentNamedItems)
    175         ownerNode()->nodeLists()->removeCacheWithAtomicName(this, type());
    176 }
    177 
    178 template <class NodeListType>
    179 inline bool isMatchingElement(const NodeListType*, Element*);
    180 
    181 template <> inline bool isMatchingElement(const HTMLCollection* htmlCollection, Element* element)
    182 {
    183     CollectionType type = htmlCollection->type();
    184     if (!element->isHTMLElement() && !(type == DocAll || type == NodeChildren))
    185         return false;
    186 
    187     switch (type) {
    188     case DocImages:
    189         return element->hasLocalName(imgTag);
    190     case DocScripts:
    191         return element->hasLocalName(scriptTag);
    192     case DocForms:
    193         return element->hasLocalName(formTag);
    194     case TableTBodies:
    195         return element->hasLocalName(tbodyTag);
    196     case TRCells:
    197         return element->hasLocalName(tdTag) || element->hasLocalName(thTag);
    198     case TSectionRows:
    199         return element->hasLocalName(trTag);
    200     case SelectOptions:
    201         return element->hasLocalName(optionTag);
    202     case SelectedOptions:
    203         return element->hasLocalName(optionTag) && toHTMLOptionElement(element)->selected();
    204     case DataListOptions:
    205         if (element->hasLocalName(optionTag)) {
    206             HTMLOptionElement* option = toHTMLOptionElement(element);
    207             if (!option->isDisabledFormControl() && !option->value().isEmpty())
    208                 return true;
    209         }
    210         return false;
    211     case MapAreas:
    212         return element->hasLocalName(areaTag);
    213     case DocApplets:
    214         return element->hasLocalName(appletTag) || (element->hasLocalName(objectTag) && static_cast<HTMLObjectElement*>(element)->containsJavaApplet());
    215     case DocEmbeds:
    216         return element->hasLocalName(embedTag);
    217     case DocLinks:
    218         return (element->hasLocalName(aTag) || element->hasLocalName(areaTag)) && element->fastHasAttribute(hrefAttr);
    219     case DocAnchors:
    220         return element->hasLocalName(aTag) && element->fastHasAttribute(nameAttr);
    221     case DocAll:
    222     case NodeChildren:
    223         return true;
    224     case FormControls:
    225     case DocumentNamedItems:
    226     case TableRows:
    227     case WindowNamedItems:
    228     case ChildNodeListType:
    229     case ClassNodeListType:
    230     case NameNodeListType:
    231     case TagNodeListType:
    232     case HTMLTagNodeListType:
    233     case RadioNodeListType:
    234     case LabelsNodeListType:
    235         ASSERT_NOT_REACHED();
    236     }
    237     return false;
    238 }
    239 
    240 template <> inline bool isMatchingElement(const LiveNodeList* nodeList, Element* element)
    241 {
    242     return nodeList->nodeMatches(element);
    243 }
    244 
    245 template <> inline bool isMatchingElement(const HTMLTagNodeList* nodeList, Element* element)
    246 {
    247     return nodeList->nodeMatchesInlined(element);
    248 }
    249 
    250 template <> inline bool isMatchingElement(const ClassNodeList* nodeList, Element* element)
    251 {
    252     return nodeList->nodeMatchesInlined(element);
    253 }
    254 
    255 static Node* previousNode(Node* base, Node* previous, bool onlyIncludeDirectChildren)
    256 {
    257     return onlyIncludeDirectChildren ? previous->previousSibling() : NodeTraversal::previous(previous, base);
    258 }
    259 
    260 static inline Node* lastDescendent(Node* node)
    261 {
    262     node = node->lastChild();
    263     for (Node* current = node; current; current = current->lastChild())
    264         node = current;
    265     return node;
    266 }
    267 
    268 static Node* lastNode(Node* rootNode, bool onlyIncludeDirectChildren)
    269 {
    270     return onlyIncludeDirectChildren ? rootNode->lastChild() : lastDescendent(rootNode);
    271 }
    272 
    273 ALWAYS_INLINE Node* LiveNodeListBase::iterateForPreviousNode(Node* current) const
    274 {
    275     bool onlyIncludeDirectChildren = shouldOnlyIncludeDirectChildren();
    276     CollectionType collectionType = type();
    277     Node* rootNode = this->rootNode();
    278     for (; current; current = previousNode(rootNode, current, onlyIncludeDirectChildren)) {
    279         if (isNodeList(collectionType)) {
    280             if (current->isElementNode() && isMatchingElement(static_cast<const LiveNodeList*>(this), toElement(current)))
    281                 return toElement(current);
    282         } else {
    283             if (current->isElementNode() && isMatchingElement(static_cast<const HTMLCollection*>(this), toElement(current)))
    284                 return toElement(current);
    285         }
    286     }
    287     return 0;
    288 }
    289 
    290 ALWAYS_INLINE Node* LiveNodeListBase::itemBefore(Node* previous) const
    291 {
    292     Node* current;
    293     if (LIKELY(!!previous)) // Without this LIKELY, length() and item() can be 10% slower.
    294         current = previousNode(rootNode(), previous, shouldOnlyIncludeDirectChildren());
    295     else
    296         current = lastNode(rootNode(), shouldOnlyIncludeDirectChildren());
    297 
    298     if (type() == ChildNodeListType)
    299         return current;
    300     return iterateForPreviousNode(current);
    301 }
    302 
    303 template <class NodeListType>
    304 inline Element* firstMatchingElement(const NodeListType* nodeList, ContainerNode* root)
    305 {
    306     Element* element = ElementTraversal::firstWithin(root);
    307     while (element && !isMatchingElement(nodeList, element))
    308         element = ElementTraversal::next(element, root);
    309     return element;
    310 }
    311 
    312 template <class NodeListType>
    313 inline Element* nextMatchingElement(const NodeListType* nodeList, Element* current, ContainerNode* root)
    314 {
    315     do {
    316         current = ElementTraversal::next(current, root);
    317     } while (current && !isMatchingElement(nodeList, current));
    318     return current;
    319 }
    320 
    321 template <class NodeListType>
    322 inline Element* traverseMatchingElementsForwardToOffset(const NodeListType* nodeList, unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root)
    323 {
    324     ASSERT(currentOffset < offset);
    325     while ((currentElement = nextMatchingElement(nodeList, currentElement, root))) {
    326         if (++currentOffset == offset)
    327             return currentElement;
    328     }
    329     return 0;
    330 }
    331 
    332 // FIXME: This should be in ChildNodeList
    333 inline Node* LiveNodeListBase::traverseChildNodeListForwardToOffset(unsigned offset, Node* currentNode, unsigned& currentOffset) const
    334 {
    335     ASSERT(type() == ChildNodeListType);
    336     ASSERT(currentOffset < offset);
    337     while ((currentNode = currentNode->nextSibling())) {
    338         if (++currentOffset == offset)
    339             return currentNode;
    340     }
    341     return 0;
    342 }
    343 
    344 // FIXME: This should be in LiveNodeList
    345 inline Element* LiveNodeListBase::traverseLiveNodeListFirstElement(ContainerNode* root) const
    346 {
    347     ASSERT(isNodeList(type()));
    348     ASSERT(type() != ChildNodeListType);
    349     if (type() == HTMLTagNodeListType)
    350         return firstMatchingElement(static_cast<const HTMLTagNodeList*>(this), root);
    351     if (type() == ClassNodeListType)
    352         return firstMatchingElement(static_cast<const ClassNodeList*>(this), root);
    353     return firstMatchingElement(static_cast<const LiveNodeList*>(this), root);
    354 }
    355 
    356 // FIXME: This should be in LiveNodeList
    357 inline Element* LiveNodeListBase::traverseLiveNodeListForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root) const
    358 {
    359     ASSERT(isNodeList(type()));
    360     ASSERT(type() != ChildNodeListType);
    361     if (type() == HTMLTagNodeListType)
    362         return traverseMatchingElementsForwardToOffset(static_cast<const HTMLTagNodeList*>(this), offset, currentElement, currentOffset, root);
    363     if (type() == ClassNodeListType)
    364         return traverseMatchingElementsForwardToOffset(static_cast<const ClassNodeList*>(this), offset, currentElement, currentOffset, root);
    365     return traverseMatchingElementsForwardToOffset(static_cast<const LiveNodeList*>(this), offset, currentElement, currentOffset, root);
    366 }
    367 
    368 bool ALWAYS_INLINE LiveNodeListBase::isLastItemCloserThanLastOrCachedItem(unsigned offset) const
    369 {
    370     ASSERT(isLengthCacheValid());
    371     unsigned distanceFromLastItem = cachedLength() - offset;
    372     if (!isItemCacheValid())
    373         return distanceFromLastItem < offset;
    374 
    375     return cachedItemOffset() < offset && distanceFromLastItem < offset - cachedItemOffset();
    376 }
    377 
    378 bool ALWAYS_INLINE LiveNodeListBase::isFirstItemCloserThanCachedItem(unsigned offset) const
    379 {
    380     ASSERT(isItemCacheValid());
    381     if (cachedItemOffset() < offset)
    382         return false;
    383 
    384     unsigned distanceFromCachedItem = cachedItemOffset() - offset;
    385     return offset < distanceFromCachedItem;
    386 }
    387 
    388 ALWAYS_INLINE void LiveNodeListBase::setItemCache(Node* item, unsigned offset, unsigned elementsArrayOffset) const
    389 {
    390     setItemCache(item, offset);
    391     if (overridesItemAfter()) {
    392         ASSERT_WITH_SECURITY_IMPLICATION(item->isElementNode());
    393         static_cast<const HTMLCollection*>(this)->m_cachedElementsArrayOffset = elementsArrayOffset;
    394     } else
    395         ASSERT(!elementsArrayOffset);
    396 }
    397 
    398 unsigned LiveNodeListBase::length() const
    399 {
    400     if (isLengthCacheValid())
    401         return cachedLength();
    402 
    403     item(UINT_MAX);
    404     ASSERT(isLengthCacheValid());
    405 
    406     return cachedLength();
    407 }
    408 
    409 // FIXME: It is silly that these functions are in HTMLCollection.cpp.
    410 Node* LiveNodeListBase::item(unsigned offset) const
    411 {
    412     if (isItemCacheValid() && cachedItemOffset() == offset)
    413         return cachedItem();
    414 
    415     if (isLengthCacheValid() && cachedLength() <= offset)
    416         return 0;
    417 
    418     ContainerNode* root = rootContainerNode();
    419     if (!root) {
    420         // FIMXE: In someTextNode.childNodes case the root is Text. We shouldn't even make a LiveNodeList for that.
    421         setLengthCache(0);
    422         return 0;
    423     }
    424 
    425     if (isLengthCacheValid() && !overridesItemAfter() && isLastItemCloserThanLastOrCachedItem(offset)) {
    426         Node* lastItem = itemBefore(0);
    427         ASSERT(lastItem);
    428         setItemCache(lastItem, cachedLength() - 1, 0);
    429     } else if (!isItemCacheValid() || isFirstItemCloserThanCachedItem(offset) || (overridesItemAfter() && offset < cachedItemOffset())) {
    430         unsigned offsetInArray = 0;
    431         Node* firstItem;
    432         if (type() == ChildNodeListType)
    433             firstItem = root->firstChild();
    434         else if (isNodeList(type()))
    435             firstItem = traverseLiveNodeListFirstElement(root);
    436         else
    437             firstItem = static_cast<const HTMLCollection*>(this)->traverseFirstElement(offsetInArray, root);
    438 
    439         if (!firstItem) {
    440             setLengthCache(0);
    441             return 0;
    442         }
    443         setItemCache(firstItem, 0, offsetInArray);
    444         ASSERT(!cachedItemOffset());
    445     }
    446 
    447     if (cachedItemOffset() == offset)
    448         return cachedItem();
    449 
    450     return itemBeforeOrAfterCachedItem(offset, root);
    451 }
    452 
    453 inline Node* LiveNodeListBase::itemBeforeOrAfterCachedItem(unsigned offset, ContainerNode* root) const
    454 {
    455     unsigned currentOffset = cachedItemOffset();
    456     Node* currentItem = cachedItem();
    457     ASSERT(currentItem);
    458     ASSERT(currentOffset != offset);
    459 
    460     if (offset < cachedItemOffset()) {
    461         ASSERT(!overridesItemAfter());
    462         while ((currentItem = itemBefore(currentItem))) {
    463             ASSERT(currentOffset);
    464             currentOffset--;
    465             if (currentOffset == offset) {
    466                 setItemCache(currentItem, currentOffset, 0);
    467                 return currentItem;
    468             }
    469         }
    470         ASSERT_NOT_REACHED();
    471         return 0;
    472     }
    473 
    474     unsigned offsetInArray = 0;
    475     if (type() == ChildNodeListType)
    476         currentItem = traverseChildNodeListForwardToOffset(offset, currentItem, currentOffset);
    477     else if (isNodeList(type()))
    478         currentItem = traverseLiveNodeListForwardToOffset(offset, toElement(currentItem), currentOffset, root);
    479     else
    480         currentItem = static_cast<const HTMLCollection*>(this)->traverseForwardToOffset(offset, toElement(currentItem), currentOffset, offsetInArray, root);
    481 
    482     if (!currentItem) {
    483         // Did not find the item. On plus side, we now know the length.
    484         setLengthCache(currentOffset + 1);
    485         return 0;
    486     }
    487     setItemCache(currentItem, currentOffset, offsetInArray);
    488     return currentItem;
    489 }
    490 
    491 Element* HTMLCollection::virtualItemAfter(unsigned&, Element*) const
    492 {
    493     ASSERT_NOT_REACHED();
    494     return 0;
    495 }
    496 
    497 static inline bool nameShouldBeVisibleInDocumentAll(HTMLElement* element)
    498 {
    499     // The document.all collection returns only certain types of elements by name,
    500     // although it returns any type of element by id.
    501     return element->hasLocalName(appletTag)
    502         || element->hasLocalName(embedTag)
    503         || element->hasLocalName(formTag)
    504         || element->hasLocalName(imgTag)
    505         || element->hasLocalName(inputTag)
    506         || element->hasLocalName(objectTag)
    507         || element->hasLocalName(selectTag);
    508 }
    509 
    510 bool HTMLCollection::checkForNameMatch(Element* element, bool checkName, const AtomicString& name) const
    511 {
    512     if (!element->isHTMLElement())
    513         return false;
    514 
    515     HTMLElement* e = toHTMLElement(element);
    516     if (!checkName)
    517         return e->getIdAttribute() == name;
    518 
    519     if (type() == DocAll && !nameShouldBeVisibleInDocumentAll(e))
    520         return false;
    521 
    522     return e->getNameAttribute() == name && e->getIdAttribute() != name;
    523 }
    524 
    525 inline Element* firstMatchingChildElement(const HTMLCollection* nodeList, ContainerNode* root)
    526 {
    527     Element* element = ElementTraversal::firstWithin(root);
    528     while (element && !isMatchingElement(nodeList, element))
    529         element = ElementTraversal::nextSkippingChildren(element, root);
    530     return element;
    531 }
    532 
    533 inline Element* nextMatchingChildElement(const HTMLCollection* nodeList, Element* current, ContainerNode* root)
    534 {
    535     do {
    536         current = ElementTraversal::nextSkippingChildren(current, root);
    537     } while (current && !isMatchingElement(nodeList, current));
    538     return current;
    539 }
    540 
    541 inline Element* HTMLCollection::traverseFirstElement(unsigned& offsetInArray, ContainerNode* root) const
    542 {
    543     if (overridesItemAfter())
    544         return virtualItemAfter(offsetInArray, 0);
    545     ASSERT(!offsetInArray);
    546     if (shouldOnlyIncludeDirectChildren())
    547         return firstMatchingChildElement(static_cast<const HTMLCollection*>(this), root);
    548     return firstMatchingElement(static_cast<const HTMLCollection*>(this), root);
    549 }
    550 
    551 inline Element* HTMLCollection::traverseNextElement(unsigned& offsetInArray, Element* previous, ContainerNode* root) const
    552 {
    553     if (overridesItemAfter())
    554         return virtualItemAfter(offsetInArray, previous);
    555     ASSERT(!offsetInArray);
    556     if (shouldOnlyIncludeDirectChildren())
    557         return nextMatchingChildElement(this, previous, root);
    558     return nextMatchingElement(this, previous, root);
    559 }
    560 
    561 inline Element* HTMLCollection::traverseForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, unsigned& offsetInArray, ContainerNode* root) const
    562 {
    563     ASSERT(currentOffset < offset);
    564     if (overridesItemAfter()) {
    565         offsetInArray = m_cachedElementsArrayOffset;
    566         while ((currentElement = virtualItemAfter(offsetInArray, currentElement))) {
    567             if (++currentOffset == offset)
    568                 return currentElement;
    569         }
    570         return 0;
    571     }
    572     if (shouldOnlyIncludeDirectChildren()) {
    573         while ((currentElement = nextMatchingChildElement(this, currentElement, root))) {
    574             if (++currentOffset == offset)
    575                 return currentElement;
    576         }
    577         return 0;
    578     }
    579     return traverseMatchingElementsForwardToOffset(this, offset, currentElement, currentOffset, root);
    580 }
    581 
    582 Node* HTMLCollection::namedItem(const AtomicString& name) const
    583 {
    584     // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp
    585     // This method first searches for an object with a matching id
    586     // attribute. If a match is not found, the method then searches for an
    587     // object with a matching name attribute, but only on those elements
    588     // that are allowed a name attribute.
    589 
    590     ContainerNode* root = rootContainerNode();
    591     if (!root)
    592         return 0;
    593 
    594     unsigned arrayOffset = 0;
    595     unsigned i = 0;
    596     for (Element* element = traverseFirstElement(arrayOffset, root); element; element = traverseNextElement(arrayOffset, element, root)) {
    597         if (checkForNameMatch(element, /* checkName */ false, name)) {
    598             setItemCache(element, i, arrayOffset);
    599             return element;
    600         }
    601         i++;
    602     }
    603 
    604     i = 0;
    605     for (Element* element = traverseFirstElement(arrayOffset, root); element; element = traverseNextElement(arrayOffset, element, root)) {
    606         if (checkForNameMatch(element, /* checkName */ true, name)) {
    607             setItemCache(element, i, arrayOffset);
    608             return element;
    609         }
    610         i++;
    611     }
    612 
    613     return 0;
    614 }
    615 
    616 void HTMLCollection::updateNameCache() const
    617 {
    618     if (hasNameCache())
    619         return;
    620 
    621     ContainerNode* root = rootContainerNode();
    622     if (!root)
    623         return;
    624 
    625     unsigned arrayOffset = 0;
    626     for (Element* element = traverseFirstElement(arrayOffset, root); element; element = traverseNextElement(arrayOffset, element, root)) {
    627         if (!element->isHTMLElement())
    628             continue;
    629         HTMLElement* htmlElement = toHTMLElement(element);
    630         const AtomicString& idAttrVal = htmlElement->getIdAttribute();
    631         const AtomicString& nameAttrVal = htmlElement->getNameAttribute();
    632         if (!idAttrVal.isEmpty())
    633             appendIdCache(idAttrVal, htmlElement);
    634         if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && (type() != DocAll || nameShouldBeVisibleInDocumentAll(htmlElement)))
    635             appendNameCache(nameAttrVal, htmlElement);
    636     }
    637 
    638     setHasNameCache();
    639 }
    640 
    641 bool HTMLCollection::hasNamedItem(const AtomicString& name) const
    642 {
    643     if (name.isEmpty())
    644         return false;
    645 
    646     updateNameCache();
    647 
    648     if (Vector<Element*>* cache = idCache(name)) {
    649         if (!cache->isEmpty())
    650             return true;
    651     }
    652 
    653     if (Vector<Element*>* cache = nameCache(name)) {
    654         if (!cache->isEmpty())
    655             return true;
    656     }
    657 
    658     return false;
    659 }
    660 
    661 void HTMLCollection::namedItems(const AtomicString& name, Vector<RefPtr<Node> >& result) const
    662 {
    663     ASSERT(result.isEmpty());
    664     if (name.isEmpty())
    665         return;
    666 
    667     updateNameCache();
    668 
    669     Vector<Element*>* idResults = idCache(name);
    670     Vector<Element*>* nameResults = nameCache(name);
    671 
    672     for (unsigned i = 0; idResults && i < idResults->size(); ++i)
    673         result.append(idResults->at(i));
    674 
    675     for (unsigned i = 0; nameResults && i < nameResults->size(); ++i)
    676         result.append(nameResults->at(i));
    677 }
    678 
    679 PassRefPtr<NodeList> HTMLCollection::tags(const String& name)
    680 {
    681     return ownerNode()->getElementsByTagName(name);
    682 }
    683 
    684 void HTMLCollection::append(NodeCacheMap& map, const AtomicString& key, Element* element)
    685 {
    686     OwnPtr<Vector<Element*> >& vector = map.add(key.impl(), nullptr).iterator->value;
    687     if (!vector)
    688         vector = adoptPtr(new Vector<Element*>);
    689     vector->append(element);
    690 }
    691 
    692 } // namespace WebCore
    693