1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/. */
7 /* base class for nsCounterList and nsQuoteList */
9 #include "nsGenConList.h"
10 #include "nsLayoutUtils.h"
11 #include "nsIContent.h"
14 void nsGenConNode::CheckFrameAssertions() {
16 mContentIndex
< int32_t(mPseudoFrame
->StyleContent()->ContentCount()) ||
17 // Special-case for the USE node created for the legacy markers,
18 // which don't use the content property.
20 "index out of range");
21 // We allow negative values of mContentIndex for 'counter-reset' and
22 // 'counter-increment'.
24 NS_ASSERTION(mContentIndex
< 0 ||
25 mPseudoFrame
->Style()->GetPseudoType() ==
26 mozilla::PseudoStyleType::before
||
27 mPseudoFrame
->Style()->GetPseudoType() ==
28 mozilla::PseudoStyleType::after
||
29 mPseudoFrame
->Style()->GetPseudoType() ==
30 mozilla::PseudoStyleType::marker
,
31 "not CSS generated content and not counter change");
32 NS_ASSERTION(mContentIndex
< 0 ||
33 mPseudoFrame
->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT
),
34 "not generated content and not counter change");
37 void nsGenConList::Clear() {
38 // Delete entire list.
40 while (nsGenConNode
* node
= mList
.popFirst()) {
44 mLastInserted
= nullptr;
47 bool nsGenConList::DestroyNodesFor(nsIFrame
* aFrame
) {
48 // This algorithm relies on the invariant that nodes of a frame are
49 // put contiguously in the linked list. This is guaranteed because
50 // each frame is mapped to only one (nsIContent, pseudoType) pair,
51 // and the nodes in the linked list are put in the tree order based
52 // on that pair and offset inside frame.
53 nsGenConNode
* node
= mNodes
.Extract(aFrame
).valueOr(nullptr);
57 MOZ_ASSERT(node
->mPseudoFrame
== aFrame
);
59 while (node
&& node
->mPseudoFrame
== aFrame
) {
60 nsGenConNode
* nextNode
= Next(node
);
65 // Modification of the list invalidates the cached pointer.
66 mLastInserted
= nullptr;
72 * Compute the type of the pseudo and the content for the pseudo that
73 * we'll use for comparison purposes.
74 * @param aContent the content to use is stored here; it's the element
75 * that generated the pseudo, or (if not for generated content), the frame's
77 * @return -2 for ::marker, -1 for ::before, +1 for ::after, and 0 otherwise.
79 inline int32_t PseudoCompareType(nsIFrame
* aFrame
, nsIContent
** aContent
) {
80 auto pseudo
= aFrame
->Style()->GetPseudoType();
81 if (pseudo
== mozilla::PseudoStyleType::marker
) {
82 *aContent
= aFrame
->GetContent()->GetParent();
85 if (pseudo
== mozilla::PseudoStyleType::before
) {
86 *aContent
= aFrame
->GetContent()->GetParent();
89 if (pseudo
== mozilla::PseudoStyleType::after
) {
90 *aContent
= aFrame
->GetContent()->GetParent();
93 *aContent
= aFrame
->GetContent();
98 bool nsGenConList::NodeAfter(const nsGenConNode
* aNode1
,
99 const nsGenConNode
* aNode2
) {
100 nsIFrame
* frame1
= aNode1
->mPseudoFrame
;
101 nsIFrame
* frame2
= aNode2
->mPseudoFrame
;
102 if (frame1
== frame2
) {
103 NS_ASSERTION(aNode2
->mContentIndex
!= aNode1
->mContentIndex
, "identical");
104 return aNode1
->mContentIndex
> aNode2
->mContentIndex
;
106 nsIContent
* content1
;
107 nsIContent
* content2
;
108 int32_t pseudoType1
= PseudoCompareType(frame1
, &content1
);
109 int32_t pseudoType2
= PseudoCompareType(frame2
, &content2
);
110 if (content1
== content2
) {
111 NS_ASSERTION(pseudoType1
!= pseudoType2
, "identical");
112 if (pseudoType1
== 0 || pseudoType2
== 0) {
113 return pseudoType2
== 0;
115 return pseudoType1
> pseudoType2
;
118 // Two pseudo-elements of different elements, we want to treat them as if
119 // they were normal elements and just use tree order.
120 content1
= frame1
->GetContent();
121 content2
= frame2
->GetContent();
123 int32_t cmp
= nsLayoutUtils::CompareTreePosition(content1
, content2
);
124 MOZ_ASSERT(cmp
!= 0, "same content, different frames");
128 nsGenConNode
* nsGenConList::BinarySearch(
129 const mozilla::FunctionRef
<bool(nsGenConNode
*)>& aIsAfter
) {
130 if (mList
.isEmpty()) {
134 // The range of indices at which |aNode| could end up.
135 // (We already know it can't be at index mSize.)
136 uint32_t first
= 0, last
= mSize
- 1;
138 // A cursor to avoid walking more than the length of the list.
139 nsGenConNode
* curNode
= mList
.getLast();
140 uint32_t curIndex
= mSize
- 1;
142 while (first
!= last
) {
143 uint32_t test
= first
+ (last
- first
) / 2;
144 if (last
== curIndex
) {
145 for (; curIndex
!= test
; --curIndex
) curNode
= Prev(curNode
);
147 for (; curIndex
!= test
; ++curIndex
) curNode
= Next(curNode
);
150 if (aIsAfter(curNode
)) {
152 // if we exit the loop, we need curNode to be right
154 curNode
= Next(curNode
);
163 void nsGenConList::Insert(nsGenConNode
* aNode
) {
165 if (mList
.isEmpty() || NodeAfter(aNode
, mList
.getLast())) {
166 mList
.insertBack(aNode
);
167 } else if (mLastInserted
&& mLastInserted
!= mList
.getLast() &&
168 NodeAfter(aNode
, mLastInserted
) &&
169 NodeAfter(Next(mLastInserted
), aNode
)) {
170 // Fast path for inserting many consecutive nodes in one place
171 mLastInserted
->setNext(aNode
);
173 auto IsAfter
= [aNode
](nsGenConNode
* curNode
) {
174 return NodeAfter(aNode
, curNode
);
176 auto* insertionNode
= BinarySearch(IsAfter
);
177 insertionNode
->setPrevious(aNode
);
181 mLastInserted
= aNode
;
183 // Set the mapping only if it is the first node of the frame.
184 // The DEBUG blocks below are for ensuring the invariant required by
185 // nsGenConList::DestroyNodesFor. See comment there.
186 if (IsFirst(aNode
) || Prev(aNode
)->mPseudoFrame
!= aNode
->mPseudoFrame
) {
188 if (nsGenConNode
* oldFrameFirstNode
= mNodes
.Get(aNode
->mPseudoFrame
)) {
189 MOZ_ASSERT(Next(aNode
) == oldFrameFirstNode
,
190 "oldFrameFirstNode should now be immediately after "
191 "the newly-inserted one.");
193 // If the node is not the only node in the list.
194 if (!IsFirst(aNode
) || !IsLast(aNode
)) {
195 nsGenConNode
* nextNode
= Next(aNode
);
196 MOZ_ASSERT(!nextNode
|| nextNode
->mPseudoFrame
!= aNode
->mPseudoFrame
,
197 "There shouldn't exist any node for this frame.");
198 // If the node is neither the first nor the last node
199 if (!IsFirst(aNode
) && !IsLast(aNode
)) {
200 MOZ_ASSERT(Prev(aNode
)->mPseudoFrame
!= nextNode
->mPseudoFrame
,
201 "New node should not break contiguity of nodes of "
207 mNodes
.InsertOrUpdate(aNode
->mPseudoFrame
, aNode
);
210 nsGenConNode
* frameFirstNode
= mNodes
.Get(aNode
->mPseudoFrame
);
211 MOZ_ASSERT(frameFirstNode
, "There should exist node map for the frame.");
212 for (nsGenConNode
* curNode
= Prev(aNode
); curNode
!= frameFirstNode
;
213 curNode
= Prev(curNode
)) {
214 MOZ_ASSERT(curNode
->mPseudoFrame
== aNode
->mPseudoFrame
,
215 "Every node between frameFirstNode and the new node inserted "
216 "should refer to the same frame.");
217 MOZ_ASSERT(!IsFirst(curNode
),
218 "The newly-inserted node should be in a contiguous run after "
219 "frameFirstNode, thus frameFirstNode should be reached before "
220 "the first node of mList.");
225 NS_ASSERTION(IsFirst(aNode
) || NodeAfter(aNode
, Prev(aNode
)),
227 NS_ASSERTION(IsLast(aNode
) || NodeAfter(Next(aNode
), aNode
), "sorting error");