/work/obj-fuzz/dist/include/mozilla/CSSOrderAwareFrameIterator.h
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | /* Iterator class for frame lists that respect CSS "order" during layout */ |
8 | | |
9 | | #ifndef mozilla_CSSOrderAwareFrameIterator_h |
10 | | #define mozilla_CSSOrderAwareFrameIterator_h |
11 | | |
12 | | #include <algorithm> |
13 | | #include "nsFrameList.h" |
14 | | #include "nsIFrame.h" |
15 | | #include "mozilla/Maybe.h" |
16 | | #include "mozilla/Assertions.h" |
17 | | |
18 | | #if defined(__clang__) && __clang_major__ == 3 && __clang_minor__ <= 9 |
19 | | #define CLANG_CRASH_BUG 1 |
20 | | #endif |
21 | | |
22 | | namespace mozilla { |
23 | | |
24 | | /** |
25 | | * CSSOrderAwareFrameIteratorT is a base class for iterators that traverse |
26 | | * child frame lists in a way that respects their CSS "order" property. |
27 | | * https://drafts.csswg.org/css-flexbox-1/#order-property |
28 | | * This class isn't meant to be directly used; instead, use its specializations |
29 | | * CSSOrderAwareFrameIterator and ReverseCSSOrderAwareFrameIterator. |
30 | | * |
31 | | * Client code can use a CSSOrderAwareFrameIterator to traverse lower-"order" |
32 | | * frames before higher-"order" ones (as required for correct flex/grid |
33 | | * layout), without modifying the frames' actual ordering within the frame |
34 | | * tree. Any frames with equal "order" values will be traversed consecutively, |
35 | | * in frametree order (which is generally equivalent to DOM order). |
36 | | * |
37 | | * By default, the iterator will skip past placeholder frames during |
38 | | * iteration. You can adjust this behavior via the ChildFilter constructor arg. |
39 | | * |
40 | | * By default, the iterator will use the frames' CSS "order" property to |
41 | | * determine its traversal order. However, it can be customized to instead use |
42 | | * the (prefixed) legacy "box-ordinal-group" CSS property instead, as part of |
43 | | * emulating "display:-webkit-box" containers. This behavior can be customized |
44 | | * using the OrderingProperty constructor arg. |
45 | | * |
46 | | * A few notes on performance: |
47 | | * - If you're iterating multiple times in a row, it's a good idea to reuse |
48 | | * the same iterator (calling Reset() to start each new iteration), rather than |
49 | | * instantiating a new one each time. |
50 | | * - If you have foreknowledge of the list's orderedness, you can save some |
51 | | * time by passing eKnownOrdered or eKnownUnordered to the constructor (which |
52 | | * will skip some checks during construction). |
53 | | * |
54 | | * Warning: if the given frame list changes, it makes the iterator invalid and |
55 | | * bad things will happen if it's used further. |
56 | | */ |
57 | | template<typename Iterator> |
58 | | class CSSOrderAwareFrameIteratorT |
59 | | { |
60 | | public: |
61 | | enum OrderState { eUnknownOrder, eKnownOrdered, eKnownUnordered }; |
62 | | enum ChildFilter { eSkipPlaceholders, eIncludeAll }; |
63 | | enum OrderingProperty { |
64 | | eUseOrder, // Default behavior: use "order". |
65 | | eUseBoxOrdinalGroup // Legacy behavior: use prefixed "box-ordinal-group". |
66 | | }; |
67 | | CSSOrderAwareFrameIteratorT(nsIFrame* aContainer, |
68 | | nsIFrame::ChildListID aListID, |
69 | | ChildFilter aFilter = eSkipPlaceholders, |
70 | | OrderState aState = eUnknownOrder, |
71 | | OrderingProperty aOrderProp = eUseOrder) |
72 | | : mChildren(aContainer->GetChildList(aListID)) |
73 | | , mArrayIndex(0) |
74 | | , mItemIndex(0) |
75 | | , mSkipPlaceholders(aFilter == eSkipPlaceholders) |
76 | | #ifdef DEBUG |
77 | | , mContainer(aContainer) |
78 | | , mListID(aListID) |
79 | | #endif |
80 | 0 | { |
81 | 0 | MOZ_ASSERT(aContainer->IsFlexOrGridContainer(), |
82 | 0 | "Only use this iterator in a container that honors 'order'"); |
83 | 0 |
|
84 | 0 | size_t count = 0; |
85 | 0 | bool isOrdered = aState != eKnownUnordered; |
86 | 0 | if (aState == eUnknownOrder) { |
87 | 0 | auto maxOrder = std::numeric_limits<int32_t>::min(); |
88 | 0 | for (auto child : mChildren) { |
89 | 0 | ++count; |
90 | 0 |
|
91 | 0 | int32_t order; |
92 | 0 | if (aOrderProp == eUseBoxOrdinalGroup) { |
93 | 0 | // We'll be using mBoxOrdinal, which has type uint32_t. However, the |
94 | 0 | // modern 'order' property (whose functionality we're co-opting) has |
95 | 0 | // type int32_t. So: if we happen to have a uint32_t value that's |
96 | 0 | // greater than INT32_MAX, we clamp it rather than letting it |
97 | 0 | // overflow. Chances are, this is just an author using BIG_VALUE |
98 | 0 | // anyway, so the clamped value should be fine. |
99 | 0 | uint32_t clampedBoxOrdinal = |
100 | 0 | std::min(child->StyleXUL()->mBoxOrdinal, |
101 | 0 | static_cast<uint32_t>(INT32_MAX)); |
102 | 0 | order = static_cast<int32_t>(clampedBoxOrdinal); |
103 | 0 | } else { |
104 | 0 | order = child->StylePosition()->mOrder; |
105 | 0 | } |
106 | 0 |
|
107 | 0 | if (order < maxOrder) { |
108 | 0 | isOrdered = false; |
109 | 0 | break; |
110 | 0 | } |
111 | 0 | maxOrder = order; |
112 | 0 | } |
113 | 0 | } |
114 | 0 | if (isOrdered) { |
115 | 0 | mIter.emplace(begin(mChildren)); |
116 | 0 | mIterEnd.emplace(end(mChildren)); |
117 | 0 | } else { |
118 | 0 | count *= 2; // XXX somewhat arbitrary estimate for now... |
119 | 0 | mArray.emplace(count); |
120 | 0 | for (Iterator i(begin(mChildren)), iEnd(end(mChildren)); i != iEnd; ++i) { |
121 | 0 | mArray->AppendElement(*i); |
122 | 0 | } |
123 | 0 | auto comparator = (aOrderProp == eUseBoxOrdinalGroup) |
124 | 0 | ? CSSBoxOrdinalGroupComparator |
125 | 0 | : CSSOrderComparator; |
126 | 0 |
|
127 | 0 | // XXX replace this with nsTArray::StableSort when bug 1147091 is fixed. |
128 | 0 | std::stable_sort(mArray->begin(), mArray->end(), comparator); |
129 | 0 | } |
130 | 0 |
|
131 | 0 | if (mSkipPlaceholders) { |
132 | 0 | SkipPlaceholders(); |
133 | 0 | } |
134 | 0 | } Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<nsFrameList::Iterator>::CSSOrderAwareFrameIteratorT(nsIFrame*, mozilla::layout::FrameChildListID, mozilla::CSSOrderAwareFrameIteratorT<nsFrameList::Iterator>::ChildFilter, mozilla::CSSOrderAwareFrameIteratorT<nsFrameList::Iterator>::OrderState, mozilla::CSSOrderAwareFrameIteratorT<nsFrameList::Iterator>::OrderingProperty) Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<mozilla::ReverseIterator<nsFrameList::Iterator> >::CSSOrderAwareFrameIteratorT(nsIFrame*, mozilla::layout::FrameChildListID, mozilla::CSSOrderAwareFrameIteratorT<mozilla::ReverseIterator<nsFrameList::Iterator> >::ChildFilter, mozilla::CSSOrderAwareFrameIteratorT<mozilla::ReverseIterator<nsFrameList::Iterator> >::OrderState, mozilla::CSSOrderAwareFrameIteratorT<mozilla::ReverseIterator<nsFrameList::Iterator> >::OrderingProperty) |
135 | | ~CSSOrderAwareFrameIteratorT() |
136 | 0 | { |
137 | 0 | MOZ_ASSERT(IsForward() == mItemCount.isNothing()); |
138 | 0 | } Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<nsFrameList::Iterator>::~CSSOrderAwareFrameIteratorT() Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<mozilla::ReverseIterator<nsFrameList::Iterator> >::~CSSOrderAwareFrameIteratorT() |
139 | | |
140 | | bool IsForward() const; |
141 | | Iterator begin(const nsFrameList& aList); |
142 | | Iterator end(const nsFrameList& aList); |
143 | | |
144 | | nsIFrame* operator*() const |
145 | 0 | { |
146 | 0 | MOZ_ASSERT(!AtEnd()); |
147 | 0 | if (mIter.isSome()) { |
148 | 0 | return **mIter; |
149 | 0 | } |
150 | 0 | return (*mArray)[mArrayIndex]; |
151 | 0 | } Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<nsFrameList::Iterator>::operator*() const Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<mozilla::ReverseIterator<nsFrameList::Iterator> >::operator*() const |
152 | | |
153 | | /** |
154 | | * Return the child index of the current item, placeholders not counted. |
155 | | * It's forbidden to call this method when the current frame is placeholder. |
156 | | */ |
157 | | size_t ItemIndex() const |
158 | 0 | { |
159 | 0 | MOZ_ASSERT(!AtEnd()); |
160 | 0 | MOZ_ASSERT(!(**this)->IsPlaceholderFrame(), |
161 | 0 | "MUST not call this when at a placeholder"); |
162 | 0 | MOZ_ASSERT(IsForward() || mItemIndex < *mItemCount, |
163 | 0 | "Returning an out-of-range mItemIndex..."); |
164 | 0 | return mItemIndex; |
165 | 0 | } Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<nsFrameList::Iterator>::ItemIndex() const Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<mozilla::ReverseIterator<nsFrameList::Iterator> >::ItemIndex() const |
166 | | |
167 | | void SetItemCount(size_t aItemCount) |
168 | 0 | { |
169 | 0 | #ifndef CLANG_CRASH_BUG |
170 | 0 | MOZ_ASSERT(mIter.isSome() || aItemCount <= mArray->Length(), |
171 | 0 | "item count mismatch"); |
172 | 0 | #endif |
173 | 0 | mItemCount.emplace(aItemCount); |
174 | 0 | // Note: it's OK if mItemIndex underflows -- ItemIndex() |
175 | 0 | // will not be called unless there is at least one item. |
176 | 0 | mItemIndex = IsForward() ? 0 : *mItemCount - 1; |
177 | 0 | } |
178 | | |
179 | | /** |
180 | | * Skip over placeholder children. |
181 | | */ |
182 | | void SkipPlaceholders() |
183 | 0 | { |
184 | 0 | if (mIter.isSome()) { |
185 | 0 | for (; *mIter != *mIterEnd; ++*mIter) { |
186 | 0 | nsIFrame* child = **mIter; |
187 | 0 | if (!child->IsPlaceholderFrame()) { |
188 | 0 | return; |
189 | 0 | } |
190 | 0 | } |
191 | 0 | } else { |
192 | 0 | for (; mArrayIndex < mArray->Length(); ++mArrayIndex) { |
193 | 0 | nsIFrame* child = (*mArray)[mArrayIndex]; |
194 | 0 | if (!child->IsPlaceholderFrame()) { |
195 | 0 | return; |
196 | 0 | } |
197 | 0 | } |
198 | 0 | } |
199 | 0 | } Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<nsFrameList::Iterator>::SkipPlaceholders() Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<mozilla::ReverseIterator<nsFrameList::Iterator> >::SkipPlaceholders() |
200 | | |
201 | | bool AtEnd() const |
202 | 0 | { |
203 | 0 | #ifndef CLANG_CRASH_BUG |
204 | 0 | // Clang 3.6.2 crashes when compiling this assertion: |
205 | 0 | MOZ_ASSERT(mIter.isSome() || mArrayIndex <= mArray->Length()); |
206 | 0 | #endif |
207 | 0 | return mIter ? (*mIter == *mIterEnd) : mArrayIndex >= mArray->Length(); |
208 | 0 | } Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<nsFrameList::Iterator>::AtEnd() const Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<mozilla::ReverseIterator<nsFrameList::Iterator> >::AtEnd() const |
209 | | |
210 | | void Next() |
211 | 0 | { |
212 | | #ifdef DEBUG |
213 | | MOZ_ASSERT(!AtEnd()); |
214 | | nsFrameList list = mContainer->GetChildList(mListID); |
215 | | MOZ_ASSERT(list.FirstChild() == mChildren.FirstChild() && |
216 | | list.LastChild() == mChildren.LastChild(), |
217 | | "the list of child frames must not change while iterating!"); |
218 | | #endif |
219 | 0 | if (mSkipPlaceholders || !(**this)->IsPlaceholderFrame()) { |
220 | 0 | IsForward() ? ++mItemIndex : --mItemIndex; |
221 | 0 | } |
222 | 0 | if (mIter.isSome()) { |
223 | 0 | ++*mIter; |
224 | 0 | } else { |
225 | 0 | ++mArrayIndex; |
226 | 0 | } |
227 | 0 | if (mSkipPlaceholders) { |
228 | 0 | SkipPlaceholders(); |
229 | 0 | } |
230 | 0 | } Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<nsFrameList::Iterator>::Next() Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<mozilla::ReverseIterator<nsFrameList::Iterator> >::Next() |
231 | | |
232 | | void Reset(ChildFilter aFilter = eSkipPlaceholders) |
233 | 0 | { |
234 | 0 | if (mIter.isSome()) { |
235 | 0 | mIter.reset(); |
236 | 0 | mIter.emplace(begin(mChildren)); |
237 | 0 | mIterEnd.reset(); |
238 | 0 | mIterEnd.emplace(end(mChildren)); |
239 | 0 | } else { |
240 | 0 | mArrayIndex = 0; |
241 | 0 | } |
242 | 0 | mItemIndex = IsForward() ? 0 : *mItemCount - 1; |
243 | 0 | mSkipPlaceholders = aFilter == eSkipPlaceholders; |
244 | 0 | if (mSkipPlaceholders) { |
245 | 0 | SkipPlaceholders(); |
246 | 0 | } |
247 | 0 | } Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<nsFrameList::Iterator>::Reset(mozilla::CSSOrderAwareFrameIteratorT<nsFrameList::Iterator>::ChildFilter) Unexecuted instantiation: mozilla::CSSOrderAwareFrameIteratorT<mozilla::ReverseIterator<nsFrameList::Iterator> >::Reset(mozilla::CSSOrderAwareFrameIteratorT<mozilla::ReverseIterator<nsFrameList::Iterator> >::ChildFilter) |
248 | | |
249 | | bool IsValid() const { return mIter.isSome() || mArray.isSome(); } |
250 | | |
251 | | void Invalidate() |
252 | 0 | { |
253 | 0 | mIter.reset(); |
254 | 0 | mArray.reset(); |
255 | 0 | mozWritePoison(&mChildren, sizeof(mChildren)); |
256 | 0 | } |
257 | | |
258 | 0 | bool ItemsAreAlreadyInOrder() const { return mIter.isSome(); } |
259 | | |
260 | | static bool CSSOrderComparator(nsIFrame* const& a, nsIFrame* const& b); |
261 | | static bool CSSBoxOrdinalGroupComparator(nsIFrame* const& a, nsIFrame* const& b); |
262 | | private: |
263 | | nsFrameList mChildren; |
264 | | // Used if child list is already in ascending 'order'. |
265 | | Maybe<Iterator> mIter; |
266 | | Maybe<Iterator> mIterEnd; |
267 | | // Used if child list is *not* in ascending 'order'. |
268 | | // This array is pre-sorted in reverse order for a reverse iterator. |
269 | | Maybe<nsTArray<nsIFrame*>> mArray; |
270 | | size_t mArrayIndex; |
271 | | // The index of the current item (placeholders excluded). |
272 | | size_t mItemIndex; |
273 | | // The number of items (placeholders excluded). |
274 | | // It's only initialized and used in a reverse iterator. |
275 | | Maybe<size_t> mItemCount; |
276 | | // Skip placeholder children in the iteration? |
277 | | bool mSkipPlaceholders; |
278 | | #ifdef DEBUG |
279 | | nsIFrame* mContainer; |
280 | | nsIFrame::ChildListID mListID; |
281 | | #endif |
282 | | }; |
283 | | |
284 | | typedef CSSOrderAwareFrameIteratorT<nsFrameList::iterator> |
285 | | CSSOrderAwareFrameIterator; |
286 | | typedef CSSOrderAwareFrameIteratorT<nsFrameList::reverse_iterator> |
287 | | ReverseCSSOrderAwareFrameIterator; |
288 | | |
289 | | } // namespace mozilla |
290 | | |
291 | | #endif // mozilla_CSSOrderAwareFrameIterator_h |