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() {
41 if (mOffEdge
) return nullptr;
46 bool nsFrameIterator::IsDone() { return mOffEdge
!= 0; }
48 void nsFrameIterator::First() { mCurrent
= mStart
; }
50 static bool IsRootFrame(nsIFrame
* aFrame
) { return aFrame
->IsCanvasFrame(); }
52 void nsFrameIterator::Last() {
54 nsIFrame
* parent
= GetCurrent();
55 // If the current frame is a popup, don't move farther up the tree.
56 // Otherwise, get the nearest root frame or popup.
57 if (mSkipPopupChecks
|| !parent
->IsMenuPopupFrame()) {
58 while (!IsRootFrame(parent
) && (result
= GetParentFrameNotPopup(parent
)))
62 while ((result
= GetLastChild(parent
))) {
67 if (!parent
) SetOffEdge(1);
70 void nsFrameIterator::Next() {
71 // recursive-oid method to get next frame
72 nsIFrame
* result
= nullptr;
73 nsIFrame
* parent
= GetCurrent();
74 if (!parent
) parent
= GetLast();
76 if (mType
== Type::Leaf
) {
77 // Drill down to first leaf
78 while ((result
= GetFirstChild(parent
))) {
81 } else if (mType
== Type::PreOrder
) {
82 result
= GetFirstChild(parent
);
83 if (result
) parent
= result
;
86 if (parent
!= GetCurrent()) {
90 result
= GetNextSibling(parent
);
92 if (mType
!= Type::PreOrder
) {
94 while ((result
= GetFirstChild(parent
))) {
101 result
= GetParentFrameNotPopup(parent
);
102 if (!result
|| IsRootFrame(result
) ||
103 (mLockScroll
&& result
->IsScrollFrame())) {
107 if (mType
== Type::PostOrder
) {
121 void nsFrameIterator::Prev() {
122 // recursive-oid method to get prev frame
123 nsIFrame
* result
= nullptr;
124 nsIFrame
* parent
= GetCurrent();
125 if (!parent
) parent
= GetLast();
127 if (mType
== Type::Leaf
) {
128 // Drill down to last leaf
129 while ((result
= GetLastChild(parent
))) {
132 } else if (mType
== Type::PostOrder
) {
133 result
= GetLastChild(parent
);
134 if (result
) parent
= result
;
137 if (parent
!= GetCurrent()) {
141 result
= GetPrevSibling(parent
);
143 if (mType
!= Type::PostOrder
) {
145 while ((result
= GetLastChild(parent
))) {
152 result
= GetParentFrameNotPopup(parent
);
153 if (!result
|| IsRootFrame(result
) ||
154 (mLockScroll
&& result
->IsScrollFrame())) {
158 if (mType
== Type::PreOrder
) {
172 nsIFrame
* nsFrameIterator::GetParentFrame(nsIFrame
* aFrame
) {
173 if (mFollowOOFs
) aFrame
= GetPlaceholderFrame(aFrame
);
174 if (aFrame
== mLimiter
) return nullptr;
175 if (aFrame
) return aFrame
->GetParent();
180 nsIFrame
* nsFrameIterator::GetParentFrameNotPopup(nsIFrame
* aFrame
) {
181 if (mFollowOOFs
) aFrame
= GetPlaceholderFrame(aFrame
);
182 if (aFrame
== mLimiter
) return nullptr;
184 nsIFrame
* parent
= aFrame
->GetParent();
185 if (!IsPopupFrame(parent
)) return parent
;
191 nsIFrame
* nsFrameIterator::GetFirstChild(nsIFrame
* aFrame
) {
192 nsIFrame
* result
= GetFirstChildInner(aFrame
);
193 if (mLockScroll
&& result
&& result
->IsScrollFrame()) return nullptr;
194 if (result
&& mFollowOOFs
) {
195 result
= nsPlaceholderFrame::GetRealFrameFor(result
);
197 if (IsPopupFrame(result
) || IsInvokerOpenPopoverFrame(result
)) {
198 result
= GetNextSibling(result
);
205 nsIFrame
* nsFrameIterator::GetLastChild(nsIFrame
* aFrame
) {
206 nsIFrame
* result
= GetLastChildInner(aFrame
);
207 if (mLockScroll
&& result
&& result
->IsScrollFrame()) return nullptr;
208 if (result
&& mFollowOOFs
) {
209 result
= nsPlaceholderFrame::GetRealFrameFor(result
);
211 if (IsPopupFrame(result
) || IsInvokerOpenPopoverFrame(result
)) {
212 result
= GetPrevSibling(result
);
219 nsIFrame
* nsFrameIterator::GetNextSibling(nsIFrame
* aFrame
) {
220 nsIFrame
* result
= nullptr;
221 if (mFollowOOFs
) aFrame
= GetPlaceholderFrame(aFrame
);
222 if (aFrame
== mLimiter
) return nullptr;
224 result
= GetNextSiblingInner(aFrame
);
225 if (result
&& mFollowOOFs
) {
226 result
= nsPlaceholderFrame::GetRealFrameFor(result
);
227 if (IsPopupFrame(result
) || IsInvokerOpenPopoverFrame(result
)) {
228 result
= GetNextSibling(result
);
236 nsIFrame
* nsFrameIterator::GetPrevSibling(nsIFrame
* aFrame
) {
237 nsIFrame
* result
= nullptr;
238 if (mFollowOOFs
) aFrame
= GetPlaceholderFrame(aFrame
);
239 if (aFrame
== mLimiter
) return nullptr;
241 result
= GetPrevSiblingInner(aFrame
);
242 if (result
&& mFollowOOFs
) {
243 result
= nsPlaceholderFrame::GetRealFrameFor(result
);
244 if (IsPopupFrame(result
) || IsInvokerOpenPopoverFrame(result
)) {
245 result
= GetPrevSibling(result
);
253 nsIFrame
* nsFrameIterator::GetFirstChildInner(nsIFrame
* aFrame
) {
254 return mVisual
? aFrame
->PrincipalChildList().GetNextVisualFor(nullptr)
255 : aFrame
->PrincipalChildList().FirstChild();
258 nsIFrame
* nsFrameIterator::GetLastChildInner(nsIFrame
* aFrame
) {
259 return mVisual
? aFrame
->PrincipalChildList().GetPrevVisualFor(nullptr)
260 : aFrame
->PrincipalChildList().LastChild();
263 nsIFrame
* nsFrameIterator::GetNextSiblingInner(nsIFrame
* aFrame
) {
265 return aFrame
->GetNextSibling();
267 nsIFrame
* parent
= GetParentFrame(aFrame
);
268 return parent
? parent
->PrincipalChildList().GetNextVisualFor(aFrame
)
272 nsIFrame
* nsFrameIterator::GetPrevSiblingInner(nsIFrame
* aFrame
) {
274 return aFrame
->GetPrevSibling();
276 nsIFrame
* parent
= GetParentFrame(aFrame
);
277 return parent
? parent
->PrincipalChildList().GetPrevVisualFor(aFrame
)
281 nsIFrame
* nsFrameIterator::GetPlaceholderFrame(nsIFrame
* aFrame
) {
282 if (MOZ_LIKELY(!aFrame
|| !aFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
))) {
285 nsIFrame
* placeholder
= aFrame
->GetPlaceholderFrame();
286 return placeholder
? placeholder
: aFrame
;
289 bool nsFrameIterator::IsPopupFrame(nsIFrame
* aFrame
) {
290 // If skipping popup checks, pretend this isn't one.
291 if (mSkipPopupChecks
) {
294 return aFrame
&& aFrame
->IsMenuPopupFrame();
297 bool nsFrameIterator::IsInvokerOpenPopoverFrame(nsIFrame
* aFrame
) {
298 if (const nsIContent
* currentContent
= aFrame
->GetContent()) {
299 if (const auto* popover
= Element::FromNode(currentContent
)) {
300 return popover
&& popover
->IsPopoverOpen() &&
301 popover
->GetPopoverData()->GetInvoker();