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 |