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 "mozilla/Assertions.h"
11 #include "nsGkAtoms.h"
13 #include "nsFrameList.h"
14 #include "nsPlaceholderFrame.h"
15 #include "nsPresContext.h"
16 #include "nsContainerFrame.h"
17 #include "mozilla/dom/Element.h"
18 #include "mozilla/dom/PopoverData.h"
20 using namespace mozilla
;
21 using namespace mozilla::dom
;
23 nsFrameIterator::nsFrameIterator(nsPresContext
* aPresContext
, nsIFrame
* aStart
,
24 Type aType
, bool aVisual
,
25 bool aLockInScrollView
, bool aFollowOOFs
,
26 bool aSkipPopupChecks
, nsIFrame
* aLimiter
)
27 : mPresContext(aPresContext
),
28 mLockScroll(aLockInScrollView
),
29 mFollowOOFs(aFollowOOFs
),
30 mSkipPopupChecks(aSkipPopupChecks
),
33 mStart(aFollowOOFs
? nsPlaceholderFrame::GetRealFrameFor(aStart
)
40 nsIFrame
* nsFrameIterator::CurrentItem() {
48 bool nsFrameIterator::IsDone() { return mOffEdge
!= 0; }
50 void nsFrameIterator::First() { mCurrent
= mStart
; }
52 static bool IsRootFrame(nsIFrame
* aFrame
) { return aFrame
->IsCanvasFrame(); }
54 void nsFrameIterator::Last() {
56 nsIFrame
* parent
= GetCurrent();
57 // If the current frame is a popup, don't move farther up the tree.
58 // Otherwise, get the nearest root frame or popup.
59 if (mSkipPopupChecks
|| !parent
->IsMenuPopupFrame()) {
60 while (!IsRootFrame(parent
) && (result
= GetParentFrameNotPopup(parent
))) {
65 while ((result
= GetLastChild(parent
))) {
75 void nsFrameIterator::Next() {
76 // recursive-oid method to get next frame
77 nsIFrame
* result
= nullptr;
78 nsIFrame
* parent
= GetCurrent();
83 if (mType
== Type::Leaf
) {
84 // Drill down to first leaf
85 while ((result
= GetFirstChild(parent
))) {
88 } else if (mType
== Type::PreOrder
) {
89 result
= GetFirstChild(parent
);
95 if (parent
!= GetCurrent()) {
99 result
= GetNextSibling(parent
);
101 if (mType
!= Type::PreOrder
) {
103 while ((result
= GetFirstChild(parent
))) {
110 result
= GetParentFrameNotPopup(parent
);
111 if (!result
|| IsRootFrame(result
) ||
112 (mLockScroll
&& result
->IsScrollContainerFrame())) {
116 if (mType
== Type::PostOrder
) {
130 void nsFrameIterator::Prev() {
131 // recursive-oid method to get prev frame
132 nsIFrame
* result
= nullptr;
133 nsIFrame
* parent
= GetCurrent();
138 if (mType
== Type::Leaf
) {
139 // Drill down to last leaf
140 while ((result
= GetLastChild(parent
))) {
143 } else if (mType
== Type::PostOrder
) {
144 result
= GetLastChild(parent
);
150 if (parent
!= GetCurrent()) {
154 result
= GetPrevSibling(parent
);
156 if (mType
!= Type::PostOrder
) {
158 while ((result
= GetLastChild(parent
))) {
165 result
= GetParentFrameNotPopup(parent
);
166 if (!result
|| IsRootFrame(result
) ||
167 (mLockScroll
&& result
->IsScrollContainerFrame())) {
171 if (mType
== Type::PreOrder
) {
185 nsIFrame
* nsFrameIterator::GetParentFrame(nsIFrame
* aFrame
) {
187 aFrame
= GetPlaceholderFrame(aFrame
);
189 if (aFrame
== mLimiter
) {
193 return aFrame
->GetParent();
199 nsIFrame
* nsFrameIterator::GetParentFrameNotPopup(nsIFrame
* aFrame
) {
201 aFrame
= GetPlaceholderFrame(aFrame
);
203 if (aFrame
== mLimiter
) {
207 nsIFrame
* parent
= aFrame
->GetParent();
208 if (!IsPopupFrame(parent
)) {
216 nsIFrame
* nsFrameIterator::GetFirstChild(nsIFrame
* aFrame
) {
217 nsIFrame
* result
= GetFirstChildInner(aFrame
);
218 if (mLockScroll
&& result
&& result
->IsScrollContainerFrame()) {
221 if (result
&& mFollowOOFs
) {
222 result
= nsPlaceholderFrame::GetRealFrameFor(result
);
224 if (IsPopupFrame(result
) || IsInvokerOpenPopoverFrame(result
)) {
225 result
= GetNextSibling(result
);
232 nsIFrame
* nsFrameIterator::GetLastChild(nsIFrame
* aFrame
) {
233 nsIFrame
* result
= GetLastChildInner(aFrame
);
234 if (mLockScroll
&& result
&& result
->IsScrollContainerFrame()) {
237 if (result
&& mFollowOOFs
) {
238 result
= nsPlaceholderFrame::GetRealFrameFor(result
);
240 if (IsPopupFrame(result
) || IsInvokerOpenPopoverFrame(result
)) {
241 result
= GetPrevSibling(result
);
248 nsIFrame
* nsFrameIterator::GetNextSibling(nsIFrame
* aFrame
) {
249 nsIFrame
* result
= nullptr;
251 aFrame
= GetPlaceholderFrame(aFrame
);
253 if (aFrame
== mLimiter
) {
257 result
= GetNextSiblingInner(aFrame
);
258 if (result
&& mFollowOOFs
) {
259 result
= nsPlaceholderFrame::GetRealFrameFor(result
);
260 if (IsPopupFrame(result
) || IsInvokerOpenPopoverFrame(result
)) {
261 result
= GetNextSibling(result
);
269 nsIFrame
* nsFrameIterator::GetPrevSibling(nsIFrame
* aFrame
) {
270 nsIFrame
* result
= nullptr;
272 aFrame
= GetPlaceholderFrame(aFrame
);
274 if (aFrame
== mLimiter
) {
278 result
= GetPrevSiblingInner(aFrame
);
279 if (result
&& mFollowOOFs
) {
280 result
= nsPlaceholderFrame::GetRealFrameFor(result
);
281 if (IsPopupFrame(result
) || IsInvokerOpenPopoverFrame(result
)) {
282 result
= GetPrevSibling(result
);
290 nsIFrame
* nsFrameIterator::GetFirstChildInner(nsIFrame
* aFrame
) {
291 return mVisual
? aFrame
->PrincipalChildList().GetNextVisualFor(nullptr)
292 : aFrame
->PrincipalChildList().FirstChild();
295 nsIFrame
* nsFrameIterator::GetLastChildInner(nsIFrame
* aFrame
) {
296 return mVisual
? aFrame
->PrincipalChildList().GetPrevVisualFor(nullptr)
297 : aFrame
->PrincipalChildList().LastChild();
300 nsIFrame
* nsFrameIterator::GetNextSiblingInner(nsIFrame
* aFrame
) {
302 return aFrame
->GetNextSibling();
304 nsIFrame
* parent
= GetParentFrame(aFrame
);
305 return parent
? parent
->PrincipalChildList().GetNextVisualFor(aFrame
)
309 nsIFrame
* nsFrameIterator::GetPrevSiblingInner(nsIFrame
* aFrame
) {
311 return aFrame
->GetPrevSibling();
313 nsIFrame
* parent
= GetParentFrame(aFrame
);
314 return parent
? parent
->PrincipalChildList().GetPrevVisualFor(aFrame
)
318 nsIFrame
* nsFrameIterator::GetPlaceholderFrame(nsIFrame
* aFrame
) {
319 if (MOZ_LIKELY(!aFrame
|| !aFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
))) {
322 nsIFrame
* placeholder
= aFrame
->GetPlaceholderFrame();
323 return placeholder
? placeholder
: aFrame
;
326 bool nsFrameIterator::IsPopupFrame(nsIFrame
* aFrame
) {
327 // If skipping popup checks, pretend this isn't one.
328 if (mSkipPopupChecks
) {
331 return aFrame
&& aFrame
->IsMenuPopupFrame();
334 bool nsFrameIterator::IsInvokerOpenPopoverFrame(nsIFrame
* aFrame
) {
335 if (const nsIContent
* currentContent
= aFrame
->GetContent()) {
336 if (const auto* popover
= Element::FromNode(currentContent
)) {
337 return popover
&& popover
->IsPopoverOpen() &&
338 popover
->GetPopoverData()->GetInvoker();