nsGenConList.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. // vim:cindent:ts=2:et:sw=2:
  3. /* This Source Code Form is subject to the terms of the Mozilla Public
  4. * License, v. 2.0. If a copy of the MPL was not distributed with this
  5. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6. /* base class for nsCounterList and nsQuoteList */
  7. #include "nsGenConList.h"
  8. #include "nsLayoutUtils.h"
  9. #include "nsIContent.h"
  10. void
  11. nsGenConList::Clear()
  12. {
  13. // Delete entire list.
  14. mNodes.Clear();
  15. while (nsGenConNode* node = mList.popFirst()) {
  16. delete node;
  17. }
  18. mSize = 0;
  19. }
  20. bool
  21. nsGenConList::DestroyNodesFor(nsIFrame* aFrame)
  22. {
  23. // This algorithm relies on the invariant that nodes of a frame are
  24. // put contiguously in the linked list. This is guaranteed because
  25. // each frame is mapped to only one (nsIContent, pseudoType) pair,
  26. // and the nodes in the linked list are put in the tree order based
  27. // on that pair and offset inside frame.
  28. nsGenConNode* node = mNodes.GetAndRemove(aFrame).valueOr(nullptr);
  29. if (!node) {
  30. return false;
  31. }
  32. MOZ_ASSERT(node->mPseudoFrame == aFrame);
  33. while (node && node->mPseudoFrame == aFrame) {
  34. nsGenConNode* nextNode = Next(node);
  35. Destroy(node);
  36. node = nextNode;
  37. }
  38. return true;
  39. }
  40. /**
  41. * Compute the type of the pseudo and the content for the pseudo that
  42. * we'll use for comparison purposes.
  43. * @param aContent the content to use is stored here; it's the element
  44. * that generated the ::before or ::after content, or (if not for generated
  45. * content), the frame's own element
  46. * @return -1 for ::before, +1 for ::after, and 0 otherwise.
  47. */
  48. inline int32_t PseudoCompareType(nsIFrame* aFrame, nsIContent** aContent)
  49. {
  50. nsIAtom *pseudo = aFrame->StyleContext()->GetPseudo();
  51. if (pseudo == nsCSSPseudoElements::before) {
  52. *aContent = aFrame->GetContent()->GetParent();
  53. return -1;
  54. }
  55. if (pseudo == nsCSSPseudoElements::after) {
  56. *aContent = aFrame->GetContent()->GetParent();
  57. return 1;
  58. }
  59. *aContent = aFrame->GetContent();
  60. return 0;
  61. }
  62. /* static */ bool
  63. nsGenConList::NodeAfter(const nsGenConNode* aNode1, const nsGenConNode* aNode2)
  64. {
  65. nsIFrame *frame1 = aNode1->mPseudoFrame;
  66. nsIFrame *frame2 = aNode2->mPseudoFrame;
  67. if (frame1 == frame2) {
  68. NS_ASSERTION(aNode2->mContentIndex != aNode1->mContentIndex, "identical");
  69. return aNode1->mContentIndex > aNode2->mContentIndex;
  70. }
  71. nsIContent *content1;
  72. nsIContent *content2;
  73. int32_t pseudoType1 = PseudoCompareType(frame1, &content1);
  74. int32_t pseudoType2 = PseudoCompareType(frame2, &content2);
  75. if (pseudoType1 == 0 || pseudoType2 == 0) {
  76. if (content1 == content2) {
  77. NS_ASSERTION(pseudoType1 != pseudoType2, "identical");
  78. return pseudoType2 == 0;
  79. }
  80. // We want to treat an element as coming before its :before (preorder
  81. // traversal), so treating both as :before now works.
  82. if (pseudoType1 == 0) pseudoType1 = -1;
  83. if (pseudoType2 == 0) pseudoType2 = -1;
  84. } else {
  85. if (content1 == content2) {
  86. NS_ASSERTION(pseudoType1 != pseudoType2, "identical");
  87. return pseudoType1 == 1;
  88. }
  89. }
  90. // XXX Switch to the frame version of DoCompareTreePosition?
  91. int32_t cmp = nsLayoutUtils::DoCompareTreePosition(content1, content2,
  92. pseudoType1, -pseudoType2);
  93. MOZ_ASSERT(cmp != 0, "same content, different frames");
  94. return cmp > 0;
  95. }
  96. void
  97. nsGenConList::Insert(nsGenConNode* aNode)
  98. {
  99. // Check for append.
  100. if (mList.isEmpty() || NodeAfter(aNode, mList.getLast())) {
  101. mList.insertBack(aNode);
  102. } else {
  103. // Binary search.
  104. // the range of indices at which |aNode| could end up.
  105. // (We already know it can't be at index mSize.)
  106. uint32_t first = 0, last = mSize - 1;
  107. // A cursor to avoid walking more than the length of the list.
  108. nsGenConNode* curNode = mList.getLast();
  109. uint32_t curIndex = mSize - 1;
  110. while (first != last) {
  111. uint32_t test = (first + last) / 2;
  112. if (last == curIndex) {
  113. for ( ; curIndex != test; --curIndex)
  114. curNode = Prev(curNode);
  115. } else {
  116. for ( ; curIndex != test; ++curIndex)
  117. curNode = Next(curNode);
  118. }
  119. if (NodeAfter(aNode, curNode)) {
  120. first = test + 1;
  121. // if we exit the loop, we need curNode to be right
  122. ++curIndex;
  123. curNode = Next(curNode);
  124. } else {
  125. last = test;
  126. }
  127. }
  128. curNode->setPrevious(aNode);
  129. }
  130. ++mSize;
  131. // Set the mapping only if it is the first node of the frame.
  132. // The DEBUG blocks below are for ensuring the invariant required by
  133. // nsGenConList::DestroyNodesFor. See comment there.
  134. if (IsFirst(aNode) ||
  135. Prev(aNode)->mPseudoFrame != aNode->mPseudoFrame) {
  136. #ifdef DEBUG
  137. if (nsGenConNode* oldFrameFirstNode = mNodes.Get(aNode->mPseudoFrame)) {
  138. MOZ_ASSERT(Next(aNode) == oldFrameFirstNode,
  139. "oldFrameFirstNode should now be immediately after "
  140. "the newly-inserted one.");
  141. } else {
  142. // If the node is not the only node in the list.
  143. if (!IsFirst(aNode) || !IsLast(aNode)) {
  144. nsGenConNode* nextNode = Next(aNode);
  145. MOZ_ASSERT(!nextNode || nextNode->mPseudoFrame != aNode->mPseudoFrame,
  146. "There shouldn't exist any node for this frame.");
  147. // If the node is neither the first nor the last node
  148. if (!IsFirst(aNode) && !IsLast(aNode)) {
  149. MOZ_ASSERT(Prev(aNode)->mPseudoFrame != nextNode->mPseudoFrame,
  150. "New node should not break contiguity of nodes of "
  151. "the same frame.");
  152. }
  153. }
  154. }
  155. #endif
  156. mNodes.Put(aNode->mPseudoFrame, aNode);
  157. } else {
  158. #ifdef DEBUG
  159. nsGenConNode* frameFirstNode = mNodes.Get(aNode->mPseudoFrame);
  160. MOZ_ASSERT(frameFirstNode, "There should exist node map for the frame.");
  161. for (nsGenConNode* curNode = Prev(aNode);
  162. curNode != frameFirstNode; curNode = Prev(curNode)) {
  163. MOZ_ASSERT(curNode->mPseudoFrame == aNode->mPseudoFrame,
  164. "Every node between frameFirstNode and the new node inserted "
  165. "should refer to the same frame.");
  166. MOZ_ASSERT(!IsFirst(curNode),
  167. "The newly-inserted node should be in a contiguous run after "
  168. "frameFirstNode, thus frameFirstNode should be reached before "
  169. "the first node of mList.");
  170. }
  171. #endif
  172. }
  173. NS_ASSERTION(IsFirst(aNode) || NodeAfter(aNode, Prev(aNode)),
  174. "sorting error");
  175. NS_ASSERTION(IsLast(aNode) || NodeAfter(Next(aNode), aNode),
  176. "sorting error");
  177. }