Bug 1717887 Part 2: Make RenderThread backed by nsIThread, with a hang monitor. r...
[gecko.git] / layout / base / nsFrameTraversal.cpp
blob22c404c4f875b9ee22343416dc21505ac691b833
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 #include "nsFrameTraversal.h"
9 #include "nsCOMPtr.h"
10 #include "nsGkAtoms.h"
12 #include "nsFrameList.h"
13 #include "nsPlaceholderFrame.h"
14 #include "nsContainerFrame.h"
16 using namespace mozilla;
18 class nsFrameIterator : public nsIFrameEnumerator {
19 public:
20 typedef nsIFrame::ChildListID ChildListID;
22 NS_DECL_ISUPPORTS
24 virtual void First() override;
25 virtual void Next() override;
26 virtual nsIFrame* CurrentItem() override;
27 virtual bool IsDone() override;
29 virtual void Last() override;
30 virtual void Prev() override;
32 nsFrameIterator(nsPresContext* aPresContext, nsIFrame* aStart,
33 nsIteratorType aType, bool aLockScroll, bool aFollowOOFs,
34 bool aSkipPopupChecks);
36 protected:
37 virtual ~nsFrameIterator() = default;
39 void setCurrent(nsIFrame* aFrame) { mCurrent = aFrame; }
40 nsIFrame* getCurrent() { return mCurrent; }
41 nsIFrame* getStart() { return mStart; }
42 nsIFrame* getLast() { return mLast; }
43 void setLast(nsIFrame* aFrame) { mLast = aFrame; }
44 int8_t getOffEdge() { return mOffEdge; }
45 void setOffEdge(int8_t aOffEdge) { mOffEdge = aOffEdge; }
48 Our own versions of the standard frame tree navigation
49 methods, which, if the iterator is following out-of-flows,
50 apply the following rules for placeholder frames:
52 - If a frame HAS a placeholder frame, getting its parent
53 gets the placeholder's parent.
55 - If a frame's first child or next/prev sibling IS a
56 placeholder frame, then we instead return the real frame.
58 - If a frame HAS a placeholder frame, getting its next/prev
59 sibling gets the placeholder frame's next/prev sibling.
61 These are all applied recursively to support multiple levels of
62 placeholders.
65 nsIFrame* GetParentFrame(nsIFrame* aFrame);
66 // like GetParentFrame but returns null once a popup frame is reached
67 nsIFrame* GetParentFrameNotPopup(nsIFrame* aFrame);
69 nsIFrame* GetFirstChild(nsIFrame* aFrame);
70 nsIFrame* GetLastChild(nsIFrame* aFrame);
72 nsIFrame* GetNextSibling(nsIFrame* aFrame);
73 nsIFrame* GetPrevSibling(nsIFrame* aFrame);
76 These methods are overridden by the bidi visual iterator to have the
77 semantics of "get first child in visual order", "get last child in visual
78 order", "get next sibling in visual order" and "get previous sibling in
79 visual order".
82 virtual nsIFrame* GetFirstChildInner(nsIFrame* aFrame);
83 virtual nsIFrame* GetLastChildInner(nsIFrame* aFrame);
85 virtual nsIFrame* GetNextSiblingInner(nsIFrame* aFrame);
86 virtual nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame);
88 /**
89 * Return the placeholder frame for aFrame if it has one, otherwise return
90 * aFrame itself.
92 nsIFrame* GetPlaceholderFrame(nsIFrame* aFrame);
93 bool IsPopupFrame(nsIFrame* aFrame);
95 nsPresContext* const mPresContext;
96 const bool mLockScroll;
97 const bool mFollowOOFs;
98 const bool mSkipPopupChecks;
99 const nsIteratorType mType;
101 private:
102 nsIFrame* const mStart;
103 nsIFrame* mCurrent;
104 nsIFrame* mLast; // the last one that was in current;
105 int8_t mOffEdge; // 0= no -1 to far prev, 1 to far next;
108 // Bidi visual iterator
109 class nsVisualIterator : public nsFrameIterator {
110 public:
111 nsVisualIterator(nsPresContext* aPresContext, nsIFrame* aStart,
112 nsIteratorType aType, bool aLockScroll, bool aFollowOOFs,
113 bool aSkipPopupChecks)
114 : nsFrameIterator(aPresContext, aStart, aType, aLockScroll, aFollowOOFs,
115 aSkipPopupChecks) {}
117 protected:
118 nsIFrame* GetFirstChildInner(nsIFrame* aFrame) override;
119 nsIFrame* GetLastChildInner(nsIFrame* aFrame) override;
121 nsIFrame* GetNextSiblingInner(nsIFrame* aFrame) override;
122 nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame) override;
125 /************IMPLEMENTATIONS**************/
127 nsresult NS_CreateFrameTraversal(nsIFrameTraversal** aResult) {
128 NS_ENSURE_ARG_POINTER(aResult);
130 nsCOMPtr<nsIFrameTraversal> t = new nsFrameTraversal();
131 t.forget(aResult);
133 return NS_OK;
136 nsresult NS_NewFrameTraversal(nsIFrameEnumerator** aEnumerator,
137 nsPresContext* aPresContext, nsIFrame* aStart,
138 nsIteratorType aType, bool aVisual,
139 bool aLockInScrollView, bool aFollowOOFs,
140 bool aSkipPopupChecks) {
141 if (!aEnumerator || !aStart) return NS_ERROR_NULL_POINTER;
143 if (aFollowOOFs) {
144 aStart = nsPlaceholderFrame::GetRealFrameFor(aStart);
147 nsCOMPtr<nsIFrameEnumerator> trav;
148 if (aVisual) {
149 trav = new nsVisualIterator(aPresContext, aStart, aType, aLockInScrollView,
150 aFollowOOFs, aSkipPopupChecks);
151 } else {
152 trav = new nsFrameIterator(aPresContext, aStart, aType, aLockInScrollView,
153 aFollowOOFs, aSkipPopupChecks);
155 trav.forget(aEnumerator);
156 return NS_OK;
159 nsFrameTraversal::nsFrameTraversal() = default;
161 nsFrameTraversal::~nsFrameTraversal() = default;
163 NS_IMPL_ISUPPORTS(nsFrameTraversal, nsIFrameTraversal)
165 NS_IMETHODIMP
166 nsFrameTraversal::NewFrameTraversal(nsIFrameEnumerator** aEnumerator,
167 nsPresContext* aPresContext,
168 nsIFrame* aStart, int32_t aType,
169 bool aVisual, bool aLockInScrollView,
170 bool aFollowOOFs, bool aSkipPopupChecks) {
171 return NS_NewFrameTraversal(aEnumerator, aPresContext, aStart,
172 static_cast<nsIteratorType>(aType), aVisual,
173 aLockInScrollView, aFollowOOFs, aSkipPopupChecks);
176 // nsFrameIterator implementation
178 NS_IMPL_ISUPPORTS(nsFrameIterator, nsIFrameEnumerator)
180 nsFrameIterator::nsFrameIterator(nsPresContext* aPresContext, nsIFrame* aStart,
181 nsIteratorType aType, bool aLockInScrollView,
182 bool aFollowOOFs, bool aSkipPopupChecks)
183 : mPresContext(aPresContext),
184 mLockScroll(aLockInScrollView),
185 mFollowOOFs(aFollowOOFs),
186 mSkipPopupChecks(aSkipPopupChecks),
187 mType(aType),
188 mStart(aStart),
189 mCurrent(aStart),
190 mLast(aStart),
191 mOffEdge(0) {
192 MOZ_ASSERT(!aFollowOOFs || !aStart->IsPlaceholderFrame(),
193 "Caller should have resolved placeholder frame");
196 nsIFrame* nsFrameIterator::CurrentItem() {
197 if (mOffEdge) return nullptr;
199 return mCurrent;
202 bool nsFrameIterator::IsDone() { return mOffEdge != 0; }
204 void nsFrameIterator::First() { mCurrent = mStart; }
206 static bool IsRootFrame(nsIFrame* aFrame) {
207 return aFrame->IsCanvasFrame() || aFrame->IsXULRootFrame();
210 void nsFrameIterator::Last() {
211 nsIFrame* result;
212 nsIFrame* parent = getCurrent();
213 // If the current frame is a popup, don't move farther up the tree.
214 // Otherwise, get the nearest root frame or popup.
215 if (mSkipPopupChecks || !parent->IsMenuPopupFrame()) {
216 while (!IsRootFrame(parent) && (result = GetParentFrameNotPopup(parent)))
217 parent = result;
220 while ((result = GetLastChild(parent))) {
221 parent = result;
224 setCurrent(parent);
225 if (!parent) setOffEdge(1);
228 void nsFrameIterator::Next() {
229 // recursive-oid method to get next frame
230 nsIFrame* result = nullptr;
231 nsIFrame* parent = getCurrent();
232 if (!parent) parent = getLast();
234 if (mType == eLeaf) {
235 // Drill down to first leaf
236 while ((result = GetFirstChild(parent))) {
237 parent = result;
239 } else if (mType == ePreOrder) {
240 result = GetFirstChild(parent);
241 if (result) parent = result;
244 if (parent != getCurrent()) {
245 result = parent;
246 } else {
247 while (parent) {
248 result = GetNextSibling(parent);
249 if (result) {
250 if (mType != ePreOrder) {
251 parent = result;
252 while ((result = GetFirstChild(parent))) {
253 parent = result;
255 result = parent;
257 break;
258 } else {
259 result = GetParentFrameNotPopup(parent);
260 if (!result || IsRootFrame(result) ||
261 (mLockScroll && result->IsScrollFrame())) {
262 result = nullptr;
263 break;
265 if (mType == ePostOrder) break;
266 parent = result;
271 setCurrent(result);
272 if (!result) {
273 setOffEdge(1);
274 setLast(parent);
278 void nsFrameIterator::Prev() {
279 // recursive-oid method to get prev frame
280 nsIFrame* result = nullptr;
281 nsIFrame* parent = getCurrent();
282 if (!parent) parent = getLast();
284 if (mType == eLeaf) {
285 // Drill down to last leaf
286 while ((result = GetLastChild(parent))) {
287 parent = result;
289 } else if (mType == ePostOrder) {
290 result = GetLastChild(parent);
291 if (result) parent = result;
294 if (parent != getCurrent()) {
295 result = parent;
296 } else {
297 while (parent) {
298 result = GetPrevSibling(parent);
299 if (result) {
300 if (mType != ePostOrder) {
301 parent = result;
302 while ((result = GetLastChild(parent))) {
303 parent = result;
305 result = parent;
307 break;
309 result = GetParentFrameNotPopup(parent);
310 if (!result || IsRootFrame(result) ||
311 (mLockScroll && result->IsScrollFrame())) {
312 result = nullptr;
313 break;
315 if (mType == ePreOrder) break;
316 parent = result;
320 setCurrent(result);
321 if (!result) {
322 setOffEdge(-1);
323 setLast(parent);
327 nsIFrame* nsFrameIterator::GetParentFrame(nsIFrame* aFrame) {
328 if (mFollowOOFs) aFrame = GetPlaceholderFrame(aFrame);
329 if (aFrame) return aFrame->GetParent();
331 return nullptr;
334 nsIFrame* nsFrameIterator::GetParentFrameNotPopup(nsIFrame* aFrame) {
335 if (mFollowOOFs) aFrame = GetPlaceholderFrame(aFrame);
336 if (aFrame) {
337 nsIFrame* parent = aFrame->GetParent();
338 if (!IsPopupFrame(parent)) return parent;
341 return nullptr;
344 nsIFrame* nsFrameIterator::GetFirstChild(nsIFrame* aFrame) {
345 nsIFrame* result = GetFirstChildInner(aFrame);
346 if (mLockScroll && result && result->IsScrollFrame()) return nullptr;
347 if (result && mFollowOOFs) {
348 result = nsPlaceholderFrame::GetRealFrameFor(result);
350 if (IsPopupFrame(result)) result = GetNextSibling(result);
352 return result;
355 nsIFrame* nsFrameIterator::GetLastChild(nsIFrame* aFrame) {
356 nsIFrame* result = GetLastChildInner(aFrame);
357 if (mLockScroll && result && result->IsScrollFrame()) return nullptr;
358 if (result && mFollowOOFs) {
359 result = nsPlaceholderFrame::GetRealFrameFor(result);
361 if (IsPopupFrame(result)) result = GetPrevSibling(result);
363 return result;
366 nsIFrame* nsFrameIterator::GetNextSibling(nsIFrame* aFrame) {
367 nsIFrame* result = nullptr;
368 if (mFollowOOFs) aFrame = GetPlaceholderFrame(aFrame);
369 if (aFrame) {
370 result = GetNextSiblingInner(aFrame);
371 if (result && mFollowOOFs)
372 result = nsPlaceholderFrame::GetRealFrameFor(result);
375 if (mFollowOOFs && IsPopupFrame(result)) result = GetNextSibling(result);
377 return result;
380 nsIFrame* nsFrameIterator::GetPrevSibling(nsIFrame* aFrame) {
381 nsIFrame* result = nullptr;
382 if (mFollowOOFs) aFrame = GetPlaceholderFrame(aFrame);
383 if (aFrame) {
384 result = GetPrevSiblingInner(aFrame);
385 if (result && mFollowOOFs)
386 result = nsPlaceholderFrame::GetRealFrameFor(result);
389 if (mFollowOOFs && IsPopupFrame(result)) result = GetPrevSibling(result);
391 return result;
394 nsIFrame* nsFrameIterator::GetFirstChildInner(nsIFrame* aFrame) {
395 return aFrame->PrincipalChildList().FirstChild();
398 nsIFrame* nsFrameIterator::GetLastChildInner(nsIFrame* aFrame) {
399 return aFrame->PrincipalChildList().LastChild();
402 nsIFrame* nsFrameIterator::GetNextSiblingInner(nsIFrame* aFrame) {
403 return aFrame->GetNextSibling();
406 nsIFrame* nsFrameIterator::GetPrevSiblingInner(nsIFrame* aFrame) {
407 return aFrame->GetPrevSibling();
410 nsIFrame* nsFrameIterator::GetPlaceholderFrame(nsIFrame* aFrame) {
411 if (MOZ_LIKELY(!aFrame || !aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))) {
412 return aFrame;
414 nsIFrame* placeholder = aFrame->GetPlaceholderFrame();
415 return placeholder ? placeholder : aFrame;
418 bool nsFrameIterator::IsPopupFrame(nsIFrame* aFrame) {
419 // If skipping popup checks, pretend this isn't one.
420 if (mSkipPopupChecks) {
421 return false;
424 return (aFrame && aFrame->StyleDisplay()->mDisplay == StyleDisplay::MozPopup);
427 // nsVisualIterator implementation
429 nsIFrame* nsVisualIterator::GetFirstChildInner(nsIFrame* aFrame) {
430 return aFrame->PrincipalChildList().GetNextVisualFor(nullptr);
433 nsIFrame* nsVisualIterator::GetLastChildInner(nsIFrame* aFrame) {
434 return aFrame->PrincipalChildList().GetPrevVisualFor(nullptr);
437 nsIFrame* nsVisualIterator::GetNextSiblingInner(nsIFrame* aFrame) {
438 nsIFrame* parent = GetParentFrame(aFrame);
439 if (!parent) return nullptr;
440 return parent->PrincipalChildList().GetNextVisualFor(aFrame);
443 nsIFrame* nsVisualIterator::GetPrevSiblingInner(nsIFrame* aFrame) {
444 nsIFrame* parent = GetParentFrame(aFrame);
445 if (!parent) return nullptr;
446 return parent->PrincipalChildList().GetPrevVisualFor(aFrame);