1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 * rendering object that is the root of the frame tree, which contains
8 * the document's scrollbars and contains fixed-positioned elements
11 #include "nsViewportFrame.h"
12 #include "nsGkAtoms.h"
13 #include "nsIScrollableFrame.h"
14 #include "nsSubDocumentFrame.h"
15 #include "nsAbsoluteContainingBlock.h"
16 #include "GeckoProfiler.h"
18 using namespace mozilla
;
21 NS_NewViewportFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
23 return new (aPresShell
) ViewportFrame(aContext
);
26 NS_IMPL_FRAMEARENA_HELPERS(ViewportFrame
)
27 NS_QUERYFRAME_HEAD(ViewportFrame
)
28 NS_QUERYFRAME_ENTRY(ViewportFrame
)
29 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
32 ViewportFrame::Init(nsIContent
* aContent
,
34 nsIFrame
* aPrevInFlow
)
36 Super::Init(aContent
, aParent
, aPrevInFlow
);
38 nsIFrame
* parent
= nsLayoutUtils::GetCrossDocParentFrame(this);
40 nsFrameState state
= parent
->GetStateBits();
42 mState
|= state
& (NS_FRAME_IN_POPUP
);
47 ViewportFrame::SetInitialChildList(ChildListID aListID
,
48 nsFrameList
& aChildList
)
50 // See which child list to add the frames to
52 nsFrame::VerifyDirtyBitSet(aChildList
);
54 return nsContainerFrame::SetInitialChildList(aListID
, aChildList
);
58 ViewportFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
59 const nsRect
& aDirtyRect
,
60 const nsDisplayListSet
& aLists
)
62 PROFILER_LABEL("ViewportFrame", "BuildDisplayList");
63 nsIFrame
* kid
= mFrames
.FirstChild();
67 // make the kid's BorderBackground our own. This ensures that the canvas
68 // frame's background becomes our own background and therefore appears
69 // below negative z-index elements.
70 BuildDisplayListForChild(aBuilder
, kid
, aDirtyRect
, aLists
);
74 ViewportFrame::AppendFrames(ChildListID aListID
,
75 nsFrameList
& aFrameList
)
77 NS_ASSERTION(aListID
== kPrincipalList
||
78 aListID
== GetAbsoluteListID(), "unexpected child list");
79 NS_ASSERTION(aListID
!= GetAbsoluteListID() ||
80 GetChildList(aListID
).IsEmpty(), "Shouldn't have any kids!");
81 return nsContainerFrame::AppendFrames(aListID
, aFrameList
);
85 ViewportFrame::InsertFrames(ChildListID aListID
,
87 nsFrameList
& aFrameList
)
89 NS_ASSERTION(aListID
== kPrincipalList
||
90 aListID
== GetAbsoluteListID(), "unexpected child list");
91 NS_ASSERTION(aListID
!= GetAbsoluteListID() ||
92 GetChildList(aListID
).IsEmpty(), "Shouldn't have any kids!");
93 return nsContainerFrame::InsertFrames(aListID
, aPrevFrame
, aFrameList
);
97 ViewportFrame::RemoveFrame(ChildListID aListID
,
100 NS_ASSERTION(aListID
== kPrincipalList
||
101 aListID
== GetAbsoluteListID(), "unexpected child list");
102 return nsContainerFrame::RemoveFrame(aListID
, aOldFrame
);
105 /* virtual */ nscoord
106 ViewportFrame::GetMinWidth(nsRenderingContext
*aRenderingContext
)
109 DISPLAY_MIN_WIDTH(this, result
);
110 if (mFrames
.IsEmpty())
113 result
= mFrames
.FirstChild()->GetMinWidth(aRenderingContext
);
118 /* virtual */ nscoord
119 ViewportFrame::GetPrefWidth(nsRenderingContext
*aRenderingContext
)
122 DISPLAY_PREF_WIDTH(this, result
);
123 if (mFrames
.IsEmpty())
126 result
= mFrames
.FirstChild()->GetPrefWidth(aRenderingContext
);
132 ViewportFrame::AdjustReflowStateForScrollbars(nsHTMLReflowState
* aReflowState
) const
134 // Get our prinicpal child frame and see if we're scrollable
135 nsIFrame
* kidFrame
= mFrames
.FirstChild();
136 nsIScrollableFrame
* scrollingFrame
= do_QueryFrame(kidFrame
);
138 if (scrollingFrame
) {
139 nsMargin scrollbars
= scrollingFrame
->GetActualScrollbarSizes();
140 aReflowState
->SetComputedWidth(aReflowState
->ComputedWidth() -
141 scrollbars
.LeftRight());
142 aReflowState
->availableWidth
-= scrollbars
.LeftRight();
143 aReflowState
->SetComputedHeightWithoutResettingResizeFlags(
144 aReflowState
->ComputedHeight() - scrollbars
.TopBottom());
145 return nsPoint(scrollbars
.left
, scrollbars
.top
);
147 return nsPoint(0, 0);
151 ViewportFrame::AdjustReflowStateAsContainingBlock(nsHTMLReflowState
* aReflowState
) const
156 AdjustReflowStateForScrollbars(aReflowState
);
158 NS_ASSERTION(GetAbsoluteContainingBlock()->GetChildList().IsEmpty() ||
159 (offset
.x
== 0 && offset
.y
== 0),
160 "We don't handle correct positioning of fixed frames with "
161 "scrollbars in odd positions");
163 // If a scroll position clamping scroll-port size has been set, layout
164 // fixed position elements to this size instead of the computed size.
165 nsRect
rect(0, 0, aReflowState
->ComputedWidth(), aReflowState
->ComputedHeight());
166 nsIPresShell
* ps
= PresContext()->PresShell();
167 if (ps
->IsScrollPositionClampingScrollPortSizeSet()) {
168 rect
.SizeTo(ps
->GetScrollPositionClampingScrollPortSize());
171 // Make sure content document fixed-position margins are respected.
172 rect
.Deflate(ps
->GetContentDocumentFixedPositionMargins());
177 ViewportFrame::Reflow(nsPresContext
* aPresContext
,
178 nsHTMLReflowMetrics
& aDesiredSize
,
179 const nsHTMLReflowState
& aReflowState
,
180 nsReflowStatus
& aStatus
)
182 DO_GLOBAL_REFLOW_COUNT("ViewportFrame");
183 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aDesiredSize
, aStatus
);
184 NS_FRAME_TRACE_REFLOW_IN("ViewportFrame::Reflow");
186 // Initialize OUT parameters
187 aStatus
= NS_FRAME_COMPLETE
;
189 // Because |Reflow| sets mComputedHeight on the child to
191 AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT
);
193 // Set our size up front, since some parts of reflow depend on it
194 // being already set. Note that the computed height may be
195 // unconstrained; that's ok. Consumers should watch out for that.
196 SetSize(nsSize(aReflowState
.ComputedWidth(), aReflowState
.ComputedHeight()));
198 // Reflow the main content first so that the placeholders of the
199 // fixed-position frames will be in the right places on an initial
201 nscoord kidHeight
= 0;
205 if (mFrames
.NotEmpty()) {
206 // Deal with a non-incremental reflow or an incremental reflow
207 // targeted at our one-and-only principal child frame.
208 if (aReflowState
.ShouldReflowAllKids() ||
209 aReflowState
.mFlags
.mVResize
||
210 NS_SUBTREE_DIRTY(mFrames
.FirstChild())) {
211 // Reflow our one-and-only principal child frame
212 nsIFrame
* kidFrame
= mFrames
.FirstChild();
213 nsHTMLReflowMetrics kidDesiredSize
;
214 nsSize
availableSpace(aReflowState
.availableWidth
,
215 aReflowState
.availableHeight
);
216 nsHTMLReflowState
kidReflowState(aPresContext
, aReflowState
,
217 kidFrame
, availableSpace
);
220 kidReflowState
.SetComputedHeight(aReflowState
.ComputedHeight());
221 rv
= ReflowChild(kidFrame
, aPresContext
, kidDesiredSize
, kidReflowState
,
223 kidHeight
= kidDesiredSize
.height
;
225 FinishReflowChild(kidFrame
, aPresContext
, nullptr, kidDesiredSize
, 0, 0, 0);
227 kidHeight
= mFrames
.FirstChild()->GetSize().height
;
231 NS_ASSERTION(aReflowState
.availableWidth
!= NS_UNCONSTRAINEDSIZE
,
232 "shouldn't happen anymore");
234 // Return the max size as our desired size
235 aDesiredSize
.width
= aReflowState
.availableWidth
;
236 // Being flowed initially at an unconstrained height means we should
237 // return our child's intrinsic size.
238 aDesiredSize
.height
= aReflowState
.ComputedHeight() != NS_UNCONSTRAINEDSIZE
239 ? aReflowState
.ComputedHeight()
241 aDesiredSize
.SetOverflowAreasToDesiredBounds();
243 if (mFrames
.NotEmpty()) {
244 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, mFrames
.FirstChild());
247 if (IsAbsoluteContainer()) {
248 // Make a copy of the reflow state and change the computed width and height
249 // to reflect the available space for the fixed items
250 nsHTMLReflowState
reflowState(aReflowState
);
252 if (reflowState
.availableHeight
== NS_UNCONSTRAINEDSIZE
) {
253 // We have an intrinsic-height document with abs-pos/fixed-pos children.
254 // Set the available height and mComputedHeight to our chosen height.
255 reflowState
.availableHeight
= aDesiredSize
.height
;
256 // Not having border/padding simplifies things
257 NS_ASSERTION(reflowState
.mComputedBorderPadding
== nsMargin(0,0,0,0),
258 "Viewports can't have border/padding");
259 reflowState
.SetComputedHeight(aDesiredSize
.height
);
262 nsRect rect
= AdjustReflowStateAsContainingBlock(&reflowState
);
264 // Just reflow all the fixed-pos frames.
265 rv
= GetAbsoluteContainingBlock()->Reflow(this, aPresContext
, reflowState
, aStatus
,
267 false, true, true, // XXX could be optimized
268 &aDesiredSize
.mOverflowAreas
);
271 // If we were dirty then do a repaint
272 if (GetStateBits() & NS_FRAME_IS_DIRTY
) {
276 // Clipping is handled by the document container (e.g., nsSubDocumentFrame),
277 // so we don't need to change our overflow areas.
278 bool overflowChanged
= FinishAndStoreOverflow(&aDesiredSize
);
279 if (overflowChanged
) {
280 // We may need to alert our container to get it to pick up the
282 nsSubDocumentFrame
* container
= static_cast<nsSubDocumentFrame
*>
283 (nsLayoutUtils::GetCrossDocParentFrame(this));
284 if (container
&& !container
->ShouldClipSubdocument()) {
285 container
->PresContext()->PresShell()->
286 FrameNeedsReflow(container
, nsIPresShell::eResize
, NS_FRAME_IS_DIRTY
);
290 NS_FRAME_TRACE_REFLOW_OUT("ViewportFrame::Reflow", aStatus
);
291 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
296 ViewportFrame::GetType() const
298 return nsGkAtoms::viewportFrame
;
303 ViewportFrame::GetFrameName(nsAString
& aResult
) const
305 return MakeFrameName(NS_LITERAL_STRING("Viewport"), aResult
);