| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) | 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| 4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All r
ights reserved. | 4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All r
ights reserved. |
| 5 * | 5 * |
| 6 * This library is free software; you can redistribute it and/or | 6 * This library is free software; you can redistribute it and/or |
| 7 * modify it under the terms of the GNU Library General Public | 7 * modify it under the terms of the GNU Library General Public |
| 8 * License as published by the Free Software Foundation; either | 8 * License as published by the Free Software Foundation; either |
| 9 * version 2 of the License, or (at your option) any later version. | 9 * version 2 of the License, or (at your option) any later version. |
| 10 * | 10 * |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 175 | 175 |
| 176 HTMLCollection::~HTMLCollection() | 176 HTMLCollection::~HTMLCollection() |
| 177 { | 177 { |
| 178 // HTMLNameCollection removes cache by itself. | 178 // HTMLNameCollection removes cache by itself. |
| 179 if (type() != WindowNamedItems && type() != DocumentNamedItems) | 179 if (type() != WindowNamedItems && type() != DocumentNamedItems) |
| 180 ownerNode()->nodeLists()->removeCacheWithAtomicName(this, type()); | 180 ownerNode()->nodeLists()->removeCacheWithAtomicName(this, type()); |
| 181 } | 181 } |
| 182 | 182 |
| 183 void HTMLCollection::invalidateCache() const | 183 void HTMLCollection::invalidateCache() const |
| 184 { | 184 { |
| 185 LiveNodeListBase::invalidateCache(); | 185 m_collectionIndexCache.invalidate(); |
| 186 invalidateIdNameCacheMaps(); | 186 invalidateIdNameCacheMaps(); |
| 187 } | 187 } |
| 188 | 188 |
| 189 template <class NodeListType> | 189 template <class NodeListType> |
| 190 inline bool isMatchingElement(const NodeListType&, const Element&); | 190 inline bool isMatchingElement(const NodeListType&, const Element&); |
| 191 | 191 |
| 192 template <> inline bool isMatchingElement(const HTMLCollection& htmlCollection,
const Element& element) | 192 template <> inline bool isMatchingElement(const HTMLCollection& htmlCollection,
const Element& element) |
| 193 { | 193 { |
| 194 CollectionType type = htmlCollection.type(); | 194 CollectionType type = htmlCollection.type(); |
| 195 if (!element.isHTMLElement() && !(type == DocAll || type == NodeChildren ||
type == WindowNamedItems)) | 195 if (!element.isHTMLElement() && !(type == DocAll || type == NodeChildren ||
type == WindowNamedItems)) |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 if (current->isElementNode() && isMatchingElement(static_cast<const
LiveNodeList&>(*this), toElement(*current))) | 292 if (current->isElementNode() && isMatchingElement(static_cast<const
LiveNodeList&>(*this), toElement(*current))) |
| 293 return toElement(current); | 293 return toElement(current); |
| 294 } else { | 294 } else { |
| 295 if (current->isElementNode() && isMatchingElement(static_cast<const
HTMLCollection&>(*this), toElement(*current))) | 295 if (current->isElementNode() && isMatchingElement(static_cast<const
HTMLCollection&>(*this), toElement(*current))) |
| 296 return toElement(current); | 296 return toElement(current); |
| 297 } | 297 } |
| 298 } | 298 } |
| 299 return 0; | 299 return 0; |
| 300 } | 300 } |
| 301 | 301 |
| 302 ALWAYS_INLINE Node* LiveNodeListBase::itemBefore(const Node* previous) const | 302 Node* LiveNodeListBase::itemBefore(const Node* previous) const |
| 303 { | 303 { |
| 304 Node* current; | 304 Node* current; |
| 305 if (LIKELY(!!previous)) // Without this LIKELY, length() and item() can be 1
0% slower. | 305 if (LIKELY(!!previous)) // Without this LIKELY, length() and item() can be 1
0% slower. |
| 306 current = previousNode(rootNode(), *previous, shouldOnlyIncludeDirectChi
ldren()); | 306 current = previousNode(rootNode(), *previous, shouldOnlyIncludeDirectChi
ldren()); |
| 307 else | 307 else |
| 308 current = lastNode(rootNode(), shouldOnlyIncludeDirectChildren()); | 308 current = lastNode(rootNode(), shouldOnlyIncludeDirectChildren()); |
| 309 | 309 |
| 310 if (type() == ChildNodeListType) | 310 if (type() == ChildNodeListType) |
| 311 return current; | 311 return current; |
| 312 return iterateForPreviousNode(current); | 312 return iterateForPreviousNode(current); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 349 Node* next = ¤tNode; | 349 Node* next = ¤tNode; |
| 350 while ((next = next->nextSibling())) { | 350 while ((next = next->nextSibling())) { |
| 351 if (++currentOffset == offset) | 351 if (++currentOffset == offset) |
| 352 return next; | 352 return next; |
| 353 } | 353 } |
| 354 return 0; | 354 return 0; |
| 355 } | 355 } |
| 356 | 356 |
| 357 // FIXME: This should be in LiveNodeList.cpp but it needs to stay here until fir
stMatchingElement() | 357 // FIXME: This should be in LiveNodeList.cpp but it needs to stay here until fir
stMatchingElement() |
| 358 // and others are moved to a separate header. | 358 // and others are moved to a separate header. |
| 359 inline Node* LiveNodeList::traverseToFirstElement(const ContainerNode& root) con
st | 359 Node* LiveNodeList::traverseToFirstElement(const ContainerNode& root) const |
| 360 { | 360 { |
| 361 ASSERT(isLiveNodeListType(type())); | 361 ASSERT(isLiveNodeListType(type())); |
| 362 switch (type()) { | 362 switch (type()) { |
| 363 case ChildNodeListType: | 363 case ChildNodeListType: |
| 364 return root.firstChild(); | 364 return root.firstChild(); |
| 365 case HTMLTagNodeListType: | 365 case HTMLTagNodeListType: |
| 366 return firstMatchingElement(static_cast<const HTMLTagNodeList&>(*this),
root); | 366 return firstMatchingElement(static_cast<const HTMLTagNodeList&>(*this),
root); |
| 367 case ClassNodeListType: | 367 case ClassNodeListType: |
| 368 return firstMatchingElement(static_cast<const ClassNodeList&>(*this), ro
ot); | 368 return firstMatchingElement(static_cast<const ClassNodeList&>(*this), ro
ot); |
| 369 default: | 369 default: |
| 370 return firstMatchingElement(static_cast<const LiveNodeList&>(*this), roo
t); | 370 return firstMatchingElement(static_cast<const LiveNodeList&>(*this), roo
t); |
| 371 } | 371 } |
| 372 } | 372 } |
| 373 | 373 |
| 374 // FIXME: This should be in LiveNodeList.cpp but it needs to stay here until tra
verseMatchingElementsForwardToOffset() | 374 // FIXME: This should be in LiveNodeList.cpp but it needs to stay here until tra
verseMatchingElementsForwardToOffset() |
| 375 // and others are moved to a separate header. | 375 // and others are moved to a separate header. |
| 376 inline Node* LiveNodeList::traverseForwardToOffset(unsigned offset, Node& curren
tNode, unsigned& currentOffset, const ContainerNode& root) const | 376 Node* LiveNodeList::traverseForwardToOffset(unsigned offset, Node& currentNode,
unsigned& currentOffset, const ContainerNode& root) const |
| 377 { | 377 { |
| 378 switch (type()) { | 378 switch (type()) { |
| 379 case ChildNodeListType: | 379 case ChildNodeListType: |
| 380 return traverseSiblingsForwardToOffset(offset, currentNode, currentOffse
t); | 380 return traverseSiblingsForwardToOffset(offset, currentNode, currentOffse
t); |
| 381 case HTMLTagNodeListType: | 381 case HTMLTagNodeListType: |
| 382 return traverseMatchingElementsForwardToOffset(static_cast<const HTMLTag
NodeList&>(*this), offset, toElement(currentNode), currentOffset, root); | 382 return traverseMatchingElementsForwardToOffset(static_cast<const HTMLTag
NodeList&>(*this), offset, toElement(currentNode), currentOffset, root); |
| 383 case ClassNodeListType: | 383 case ClassNodeListType: |
| 384 return traverseMatchingElementsForwardToOffset(static_cast<const ClassNo
deList&>(*this), offset, toElement(currentNode), currentOffset, root); | 384 return traverseMatchingElementsForwardToOffset(static_cast<const ClassNo
deList&>(*this), offset, toElement(currentNode), currentOffset, root); |
| 385 default: | 385 default: |
| 386 return traverseMatchingElementsForwardToOffset(*this, offset, toElement(
currentNode), currentOffset, root); | 386 return traverseMatchingElementsForwardToOffset(*this, offset, toElement(
currentNode), currentOffset, root); |
| 387 } | 387 } |
| 388 } | 388 } |
| 389 | 389 |
| 390 bool ALWAYS_INLINE LiveNodeListBase::isLastItemCloserThanLastOrCachedItem(unsign
ed offset) const | |
| 391 { | |
| 392 ASSERT(isLengthCacheValid()); | |
| 393 unsigned distanceFromLastItem = cachedLength() - offset; | |
| 394 if (!cachedItem()) | |
| 395 return distanceFromLastItem < offset; | |
| 396 | |
| 397 return cachedItemOffset() < offset && distanceFromLastItem < offset - cached
ItemOffset(); | |
| 398 } | |
| 399 | |
| 400 bool ALWAYS_INLINE LiveNodeListBase::isFirstItemCloserThanCachedItem(unsigned of
fset) const | |
| 401 { | |
| 402 if (cachedItemOffset() < offset) | |
| 403 return false; | |
| 404 | |
| 405 unsigned distanceFromCachedItem = cachedItemOffset() - offset; | |
| 406 return offset < distanceFromCachedItem; | |
| 407 } | |
| 408 | |
| 409 unsigned LiveNodeListBase::length() const | |
| 410 { | |
| 411 if (isLengthCacheValid()) | |
| 412 return cachedLength(); | |
| 413 | |
| 414 item(UINT_MAX); | |
| 415 ASSERT(isLengthCacheValid()); | |
| 416 | |
| 417 return cachedLength(); | |
| 418 } | |
| 419 | |
| 420 // FIXME: It is silly that these functions are in HTMLCollection.cpp. | |
| 421 Node* LiveNodeListBase::item(unsigned offset) const | |
| 422 { | |
| 423 if (cachedItem() && cachedItemOffset() == offset) | |
| 424 return cachedItem(); | |
| 425 | |
| 426 if (isLengthCacheValid() && cachedLength() <= offset) | |
| 427 return 0; | |
| 428 | |
| 429 ContainerNode& root = rootNode(); | |
| 430 if (isLengthCacheValid() && !overridesItemAfter() && isLastItemCloserThanLas
tOrCachedItem(offset)) { | |
| 431 Node* lastItem = itemBefore(0); | |
| 432 ASSERT(lastItem); | |
| 433 setItemCache(lastItem, cachedLength() - 1); | |
| 434 } else if (!cachedItem() || isFirstItemCloserThanCachedItem(offset) || (over
ridesItemAfter() && offset < cachedItemOffset())) { | |
| 435 Node* firstItem; | |
| 436 if (isLiveNodeListType(type())) | |
| 437 firstItem = static_cast<const LiveNodeList*>(this)->traverseToFirstE
lement(root); | |
| 438 else | |
| 439 firstItem = static_cast<const HTMLCollection*>(this)->traverseToFirs
tElement(root); | |
| 440 | |
| 441 if (!firstItem) { | |
| 442 setLengthCache(0); | |
| 443 return 0; | |
| 444 } | |
| 445 setItemCache(firstItem, 0); | |
| 446 ASSERT(!cachedItemOffset()); | |
| 447 } | |
| 448 | |
| 449 if (cachedItemOffset() == offset) | |
| 450 return cachedItem(); | |
| 451 | |
| 452 return itemBeforeOrAfterCachedItem(offset, root); | |
| 453 } | |
| 454 | |
| 455 inline Node* LiveNodeListBase::itemBeforeOrAfterCachedItem(unsigned offset, cons
t ContainerNode& root) const | |
| 456 { | |
| 457 unsigned currentOffset = cachedItemOffset(); | |
| 458 Node* currentItem = cachedItem(); | |
| 459 ASSERT(currentItem); | |
| 460 ASSERT(currentOffset != offset); | |
| 461 | |
| 462 if (offset < cachedItemOffset()) { | |
| 463 ASSERT(!overridesItemAfter()); | |
| 464 while ((currentItem = itemBefore(currentItem))) { | |
| 465 ASSERT(currentOffset); | |
| 466 currentOffset--; | |
| 467 if (currentOffset == offset) { | |
| 468 setItemCache(currentItem, currentOffset); | |
| 469 return currentItem; | |
| 470 } | |
| 471 } | |
| 472 ASSERT_NOT_REACHED(); | |
| 473 return 0; | |
| 474 } | |
| 475 | |
| 476 if (isLiveNodeListType(type())) | |
| 477 currentItem = static_cast<const LiveNodeList*>(this)->traverseForwardToO
ffset(offset, *currentItem, currentOffset, root); | |
| 478 else | |
| 479 currentItem = static_cast<const HTMLCollection*>(this)->traverseForwardT
oOffset(offset, toElement(*currentItem), currentOffset, root); | |
| 480 | |
| 481 if (!currentItem) { | |
| 482 // Did not find the item. On plus side, we now know the length. | |
| 483 setLengthCache(currentOffset + 1); | |
| 484 return 0; | |
| 485 } | |
| 486 setItemCache(currentItem, currentOffset); | |
| 487 return currentItem; | |
| 488 } | |
| 489 | |
| 490 Element* HTMLCollection::virtualItemAfter(Element*) const | 390 Element* HTMLCollection::virtualItemAfter(Element*) const |
| 491 { | 391 { |
| 492 ASSERT_NOT_REACHED(); | 392 ASSERT_NOT_REACHED(); |
| 493 return 0; | 393 return 0; |
| 494 } | 394 } |
| 495 | 395 |
| 496 static inline bool nameShouldBeVisibleInDocumentAll(const HTMLElement& element) | 396 static inline bool nameShouldBeVisibleInDocumentAll(const HTMLElement& element) |
| 497 { | 397 { |
| 498 // The document.all collection returns only certain types of elements by nam
e, | 398 // The document.all collection returns only certain types of elements by nam
e, |
| 499 // although it returns any type of element by id. | 399 // although it returns any type of element by id. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 531 | 431 |
| 532 inline Element* nextMatchingChildElement(const HTMLCollection& nodeList, Element
& current, const ContainerNode& root) | 432 inline Element* nextMatchingChildElement(const HTMLCollection& nodeList, Element
& current, const ContainerNode& root) |
| 533 { | 433 { |
| 534 Element* next = ¤t; | 434 Element* next = ¤t; |
| 535 do { | 435 do { |
| 536 next = ElementTraversal::nextSkippingChildren(*next, &root); | 436 next = ElementTraversal::nextSkippingChildren(*next, &root); |
| 537 } while (next && !isMatchingElement(nodeList, *next)); | 437 } while (next && !isMatchingElement(nodeList, *next)); |
| 538 return next; | 438 return next; |
| 539 } | 439 } |
| 540 | 440 |
| 541 inline Element* HTMLCollection::traverseToFirstElement(const ContainerNode& root
) const | 441 Element* HTMLCollection::traverseToFirstElement(const ContainerNode& root) const |
| 542 { | 442 { |
| 543 if (overridesItemAfter()) | 443 if (overridesItemAfter()) |
| 544 return virtualItemAfter(0); | 444 return virtualItemAfter(0); |
| 545 if (shouldOnlyIncludeDirectChildren()) | 445 if (shouldOnlyIncludeDirectChildren()) |
| 546 return firstMatchingChildElement(*this, root); | 446 return firstMatchingChildElement(*this, root); |
| 547 return firstMatchingElement(*this, root); | 447 return firstMatchingElement(*this, root); |
| 548 } | 448 } |
| 549 | 449 |
| 550 inline Element* HTMLCollection::traverseNextElement(Element& previous, const Con
tainerNode& root) const | 450 inline Element* HTMLCollection::traverseNextElement(Element& previous, const Con
tainerNode& root) const |
| 551 { | 451 { |
| 552 if (overridesItemAfter()) | 452 if (overridesItemAfter()) |
| 553 return virtualItemAfter(&previous); | 453 return virtualItemAfter(&previous); |
| 554 if (shouldOnlyIncludeDirectChildren()) | 454 if (shouldOnlyIncludeDirectChildren()) |
| 555 return nextMatchingChildElement(*this, previous, root); | 455 return nextMatchingChildElement(*this, previous, root); |
| 556 return nextMatchingElement(*this, previous, root); | 456 return nextMatchingElement(*this, previous, root); |
| 557 } | 457 } |
| 558 | 458 |
| 559 inline Element* HTMLCollection::traverseForwardToOffset(unsigned offset, Element
& currentElement, unsigned& currentOffset, const ContainerNode& root) const | 459 Element* HTMLCollection::traverseForwardToOffset(unsigned offset, Node& currentE
lement, unsigned& currentOffset, const ContainerNode& root) const |
| 560 { | 460 { |
| 561 ASSERT(currentOffset < offset); | 461 ASSERT(currentOffset < offset); |
| 562 if (overridesItemAfter()) { | 462 if (overridesItemAfter()) { |
| 563 Element* next = ¤tElement; | 463 Element* next = &toElement(currentElement); |
| 564 while ((next = virtualItemAfter(next))) { | 464 while ((next = virtualItemAfter(next))) { |
| 565 if (++currentOffset == offset) | 465 if (++currentOffset == offset) |
| 566 return next; | 466 return next; |
| 567 } | 467 } |
| 568 return 0; | 468 return 0; |
| 569 } | 469 } |
| 570 if (shouldOnlyIncludeDirectChildren()) { | 470 if (shouldOnlyIncludeDirectChildren()) { |
| 571 Element* next = ¤tElement; | 471 Element* next = &toElement(currentElement); |
| 572 while ((next = nextMatchingChildElement(*this, *next, root))) { | 472 while ((next = nextMatchingChildElement(*this, *next, root))) { |
| 573 if (++currentOffset == offset) | 473 if (++currentOffset == offset) |
| 574 return next; | 474 return next; |
| 575 } | 475 } |
| 576 return 0; | 476 return 0; |
| 577 } | 477 } |
| 578 return traverseMatchingElementsForwardToOffset(*this, offset, currentElement
, currentOffset, root); | 478 return traverseMatchingElementsForwardToOffset(*this, offset, toElement(curr
entElement), currentOffset, root); |
| 579 } | 479 } |
| 580 | 480 |
| 581 Element* HTMLCollection::namedItem(const AtomicString& name) const | 481 Element* HTMLCollection::namedItem(const AtomicString& name) const |
| 582 { | 482 { |
| 583 // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/namedit
em.asp | 483 // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/namedit
em.asp |
| 584 // This method first searches for an object with a matching id | 484 // This method first searches for an object with a matching id |
| 585 // attribute. If a match is not found, the method then searches for an | 485 // attribute. If a match is not found, the method then searches for an |
| 586 // object with a matching name attribute, but only on those elements | 486 // object with a matching name attribute, but only on those elements |
| 587 // that are allowed a name attribute. | 487 // that are allowed a name attribute. |
| 588 | 488 |
| 589 ContainerNode& root = rootNode(); | 489 ContainerNode& root = rootNode(); |
| 590 unsigned i = 0; | 490 unsigned i = 0; |
| 591 for (Element* element = traverseToFirstElement(root); element; element = tra
verseNextElement(*element, root)) { | 491 for (Element* element = traverseToFirstElement(root); element; element = tra
verseNextElement(*element, root)) { |
| 592 if (checkForNameMatch(*element, /* checkName */ false, name)) { | 492 if (checkForNameMatch(*element, /* checkName */ false, name)) { |
| 593 setItemCache(element, i); | 493 m_collectionIndexCache.setCachedNode(element, i); |
| 594 return element; | 494 return element; |
| 595 } | 495 } |
| 596 i++; | 496 i++; |
| 597 } | 497 } |
| 598 | 498 |
| 599 i = 0; | 499 i = 0; |
| 600 for (Element* element = traverseToFirstElement(root); element; element = tra
verseNextElement(*element, root)) { | 500 for (Element* element = traverseToFirstElement(root); element; element = tra
verseNextElement(*element, root)) { |
| 601 if (checkForNameMatch(*element, /* checkName */ true, name)) { | 501 if (checkForNameMatch(*element, /* checkName */ true, name)) { |
| 602 setItemCache(element, i); | 502 m_collectionIndexCache.setCachedNode(element, i); |
| 603 return element; | 503 return element; |
| 604 } | 504 } |
| 605 i++; | 505 i++; |
| 606 } | 506 } |
| 607 | 507 |
| 608 return 0; | 508 return 0; |
| 609 } | 509 } |
| 610 | 510 |
| 611 void HTMLCollection::updateNameCache() const | 511 void HTMLCollection::updateNameCache() const |
| 612 { | 512 { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 648 | 548 |
| 649 void HTMLCollection::append(NodeCacheMap& map, const AtomicString& key, Element*
element) | 549 void HTMLCollection::append(NodeCacheMap& map, const AtomicString& key, Element*
element) |
| 650 { | 550 { |
| 651 OwnPtr<Vector<Element*> >& vector = map.add(key.impl(), nullptr).iterator->v
alue; | 551 OwnPtr<Vector<Element*> >& vector = map.add(key.impl(), nullptr).iterator->v
alue; |
| 652 if (!vector) | 552 if (!vector) |
| 653 vector = adoptPtr(new Vector<Element*>); | 553 vector = adoptPtr(new Vector<Element*>); |
| 654 vector->append(element); | 554 vector->append(element); |
| 655 } | 555 } |
| 656 | 556 |
| 657 } // namespace WebCore | 557 } // namespace WebCore |
| OLD | NEW |