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