nsNthIndexCache.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /*
  6. * A class that computes and caches the indices used for :nth-* pseudo-class
  7. * matching.
  8. */
  9. #include "nsNthIndexCache.h"
  10. #include "mozilla/dom/Element.h"
  11. #include "mozilla/dom/NodeInfoInlines.h"
  12. nsNthIndexCache::nsNthIndexCache()
  13. {
  14. }
  15. nsNthIndexCache::~nsNthIndexCache()
  16. {
  17. }
  18. void
  19. nsNthIndexCache::Reset()
  20. {
  21. mCaches[0][0].clear();
  22. mCaches[0][1].clear();
  23. mCaches[1][0].clear();
  24. mCaches[1][1].clear();
  25. }
  26. inline bool
  27. nsNthIndexCache::SiblingMatchesElement(nsIContent* aSibling, Element* aElement,
  28. bool aIsOfType)
  29. {
  30. return aSibling->IsElement() &&
  31. (!aIsOfType ||
  32. aSibling->NodeInfo()->NameAndNamespaceEquals(aElement->NodeInfo()));
  33. }
  34. inline bool
  35. nsNthIndexCache::IndexDeterminedFromPreviousSibling(nsIContent* aSibling,
  36. Element* aChild,
  37. bool aIsOfType,
  38. bool aIsFromEnd,
  39. const Cache& aCache,
  40. int32_t& aResult)
  41. {
  42. if (SiblingMatchesElement(aSibling, aChild, aIsOfType)) {
  43. Cache::Ptr siblingEntry = aCache.lookup(aSibling);
  44. if (siblingEntry) {
  45. int32_t siblingIndex = siblingEntry->value();
  46. NS_ASSERTION(siblingIndex != 0,
  47. "How can a non-anonymous node have an anonymous sibling?");
  48. if (siblingIndex > 0) {
  49. // At this point, aResult is a count of how many elements matching
  50. // aChild we have seen after aSibling, including aChild itself.
  51. // |siblingIndex| is the index of aSibling.
  52. // So if aIsFromEnd, we want |aResult = siblingIndex - aResult| and
  53. // otherwise we want |aResult = siblingIndex + aResult|.
  54. aResult = siblingIndex + aResult * (1 - 2 * aIsFromEnd);
  55. return true;
  56. }
  57. }
  58. ++aResult;
  59. }
  60. return false;
  61. }
  62. int32_t
  63. nsNthIndexCache::GetNthIndex(Element* aChild, bool aIsOfType,
  64. bool aIsFromEnd, bool aCheckEdgeOnly)
  65. {
  66. if (aChild->IsRootOfAnonymousSubtree()) {
  67. return 0;
  68. }
  69. Cache& cache = mCaches[aIsOfType][aIsFromEnd];
  70. if (!cache.initialized() && !cache.init()) {
  71. // Give up and just don't match.
  72. return 0;
  73. }
  74. Cache::AddPtr entry = cache.lookupForAdd(aChild);
  75. // Default the value to -2 when adding
  76. if (!entry && !cache.add(entry, aChild, -2)) {
  77. // No good; don't match.
  78. return 0;
  79. }
  80. int32_t& slot = entry->value();
  81. if (slot != -2 && (slot != -1 || aCheckEdgeOnly)) {
  82. return slot;
  83. }
  84. int32_t result = 1;
  85. if (aCheckEdgeOnly) {
  86. // The caller only cares whether or not the result is 1, so we can
  87. // stop as soon as we see any other elements that match us.
  88. if (aIsFromEnd) {
  89. for (nsIContent *cur = aChild->GetNextSibling();
  90. cur;
  91. cur = cur->GetNextSibling()) {
  92. if (SiblingMatchesElement(cur, aChild, aIsOfType)) {
  93. result = -1;
  94. break;
  95. }
  96. }
  97. } else {
  98. for (nsIContent *cur = aChild->GetPreviousSibling();
  99. cur;
  100. cur = cur->GetPreviousSibling()) {
  101. if (SiblingMatchesElement(cur, aChild, aIsOfType)) {
  102. result = -1;
  103. break;
  104. }
  105. }
  106. }
  107. } else {
  108. // In the common case, we already have a cached index for one of
  109. // our previous siblings, so check that first.
  110. for (nsIContent *cur = aChild->GetPreviousSibling();
  111. cur;
  112. cur = cur->GetPreviousSibling()) {
  113. if (IndexDeterminedFromPreviousSibling(cur, aChild, aIsOfType,
  114. aIsFromEnd, cache, result)) {
  115. slot = result;
  116. return result;
  117. }
  118. }
  119. // Now if aIsFromEnd we lose: need to actually compute our index,
  120. // since looking at previous siblings wouldn't have told us
  121. // anything about it. Note that it doesn't make sense to do cache
  122. // lookups on our following siblings, since chances are the cache
  123. // is not primed for them.
  124. if (aIsFromEnd) {
  125. result = 1;
  126. for (nsIContent *cur = aChild->GetNextSibling();
  127. cur;
  128. cur = cur->GetNextSibling()) {
  129. if (SiblingMatchesElement(cur, aChild, aIsOfType)) {
  130. ++result;
  131. }
  132. }
  133. }
  134. }
  135. slot = result;
  136. return result;
  137. }