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
,
33 nsContainerFrame
* aParent
,
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::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
48 const nsRect
& aDirtyRect
,
49 const nsDisplayListSet
& aLists
)
51 PROFILER_LABEL("ViewportFrame", "BuildDisplayList",
52 js::ProfileEntry::Category::GRAPHICS
);
54 nsIFrame
* kid
= mFrames
.FirstChild();
58 // make the kid's BorderBackground our own. This ensures that the canvas
59 // frame's background becomes our own background and therefore appears
60 // below negative z-index elements.
61 BuildDisplayListForChild(aBuilder
, kid
, aDirtyRect
, aLists
);
66 ViewportFrame::SetInitialChildList(ChildListID aListID
,
67 nsFrameList
& aChildList
)
69 nsFrame::VerifyDirtyBitSet(aChildList
);
70 nsContainerFrame::SetInitialChildList(aListID
, aChildList
);
74 ViewportFrame::AppendFrames(ChildListID aListID
,
75 nsFrameList
& aFrameList
)
77 NS_ASSERTION(aListID
== kPrincipalList
, "unexpected child list");
78 NS_ASSERTION(GetChildList(aListID
).IsEmpty(), "Shouldn't have any kids!");
79 nsContainerFrame::AppendFrames(aListID
, aFrameList
);
83 ViewportFrame::InsertFrames(ChildListID aListID
,
85 nsFrameList
& aFrameList
)
87 NS_ASSERTION(aListID
== kPrincipalList
, "unexpected child list");
88 NS_ASSERTION(GetChildList(aListID
).IsEmpty(), "Shouldn't have any kids!");
89 nsContainerFrame::InsertFrames(aListID
, aPrevFrame
, aFrameList
);
93 ViewportFrame::RemoveFrame(ChildListID aListID
,
96 NS_ASSERTION(aListID
== kPrincipalList
, "unexpected child list");
97 nsContainerFrame::RemoveFrame(aListID
, aOldFrame
);
101 /* virtual */ nscoord
102 ViewportFrame::GetMinISize(nsRenderingContext
*aRenderingContext
)
105 DISPLAY_MIN_WIDTH(this, result
);
106 if (mFrames
.IsEmpty())
109 result
= mFrames
.FirstChild()->GetMinISize(aRenderingContext
);
114 /* virtual */ nscoord
115 ViewportFrame::GetPrefISize(nsRenderingContext
*aRenderingContext
)
118 DISPLAY_PREF_WIDTH(this, result
);
119 if (mFrames
.IsEmpty())
122 result
= mFrames
.FirstChild()->GetPrefISize(aRenderingContext
);
128 ViewportFrame::AdjustReflowStateForScrollbars(nsHTMLReflowState
* aReflowState
) const
130 // Get our prinicpal child frame and see if we're scrollable
131 nsIFrame
* kidFrame
= mFrames
.FirstChild();
132 nsIScrollableFrame
* scrollingFrame
= do_QueryFrame(kidFrame
);
134 if (scrollingFrame
) {
135 nsMargin scrollbars
= scrollingFrame
->GetActualScrollbarSizes();
136 aReflowState
->SetComputedWidth(aReflowState
->ComputedWidth() -
137 scrollbars
.LeftRight());
138 aReflowState
->AvailableWidth() -= scrollbars
.LeftRight();
139 aReflowState
->SetComputedHeightWithoutResettingResizeFlags(
140 aReflowState
->ComputedHeight() - scrollbars
.TopBottom());
141 return nsPoint(scrollbars
.left
, scrollbars
.top
);
143 return nsPoint(0, 0);
147 ViewportFrame::AdjustReflowStateAsContainingBlock(nsHTMLReflowState
* aReflowState
) const
152 AdjustReflowStateForScrollbars(aReflowState
);
154 NS_ASSERTION(GetAbsoluteContainingBlock()->GetChildList().IsEmpty() ||
155 (offset
.x
== 0 && offset
.y
== 0),
156 "We don't handle correct positioning of fixed frames with "
157 "scrollbars in odd positions");
159 // If a scroll position clamping scroll-port size has been set, layout
160 // fixed position elements to this size instead of the computed size.
161 nsRect
rect(0, 0, aReflowState
->ComputedWidth(), aReflowState
->ComputedHeight());
162 nsIPresShell
* ps
= PresContext()->PresShell();
163 if (ps
->IsScrollPositionClampingScrollPortSizeSet()) {
164 rect
.SizeTo(ps
->GetScrollPositionClampingScrollPortSize());
167 // Make sure content document fixed-position margins are respected.
168 rect
.Deflate(ps
->GetContentDocumentFixedPositionMargins());
173 ViewportFrame::Reflow(nsPresContext
* aPresContext
,
174 nsHTMLReflowMetrics
& aDesiredSize
,
175 const nsHTMLReflowState
& aReflowState
,
176 nsReflowStatus
& aStatus
)
178 DO_GLOBAL_REFLOW_COUNT("ViewportFrame");
179 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aDesiredSize
, aStatus
);
180 NS_FRAME_TRACE_REFLOW_IN("ViewportFrame::Reflow");
182 // Initialize OUT parameters
183 aStatus
= NS_FRAME_COMPLETE
;
185 // Because |Reflow| sets mComputedHeight on the child to
187 AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT
);
189 // Set our size up front, since some parts of reflow depend on it
190 // being already set. Note that the computed height may be
191 // unconstrained; that's ok. Consumers should watch out for that.
192 SetSize(nsSize(aReflowState
.ComputedWidth(), aReflowState
.ComputedHeight()));
194 // Reflow the main content first so that the placeholders of the
195 // fixed-position frames will be in the right places on an initial
197 nscoord kidBSize
= 0;
198 WritingMode wm
= aReflowState
.GetWritingMode();
200 if (mFrames
.NotEmpty()) {
201 // Deal with a non-incremental reflow or an incremental reflow
202 // targeted at our one-and-only principal child frame.
203 if (aReflowState
.ShouldReflowAllKids() ||
204 aReflowState
.mFlags
.mVResize
||
205 NS_SUBTREE_DIRTY(mFrames
.FirstChild())) {
206 // Reflow our one-and-only principal child frame
207 nsIFrame
* kidFrame
= mFrames
.FirstChild();
208 nsHTMLReflowMetrics
kidDesiredSize(aReflowState
);
209 WritingMode wm
= kidFrame
->GetWritingMode();
210 LogicalSize availableSpace
= aReflowState
.AvailableSize(wm
);
211 nsHTMLReflowState
kidReflowState(aPresContext
, aReflowState
,
212 kidFrame
, availableSpace
);
215 kidReflowState
.SetComputedHeight(aReflowState
.ComputedHeight());
216 ReflowChild(kidFrame
, aPresContext
, kidDesiredSize
, kidReflowState
,
218 kidBSize
= kidDesiredSize
.BSize(wm
);
220 FinishReflowChild(kidFrame
, aPresContext
, kidDesiredSize
, nullptr, 0, 0, 0);
222 kidBSize
= LogicalSize(wm
, mFrames
.FirstChild()->GetSize()).BSize(wm
);
226 NS_ASSERTION(aReflowState
.AvailableWidth() != NS_UNCONSTRAINEDSIZE
,
227 "shouldn't happen anymore");
229 // Return the max size as our desired size
230 LogicalSize
maxSize(wm
, aReflowState
.AvailableISize(),
231 // Being flowed initially at an unconstrained block size
232 // means we should return our child's intrinsic size.
233 aReflowState
.ComputedBSize() != NS_UNCONSTRAINEDSIZE
234 ? aReflowState
.ComputedBSize()
236 aDesiredSize
.SetSize(wm
, maxSize
);
237 aDesiredSize
.SetOverflowAreasToDesiredBounds();
239 if (mFrames
.NotEmpty()) {
240 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, mFrames
.FirstChild());
243 if (IsAbsoluteContainer()) {
244 // Make a copy of the reflow state and change the computed width and height
245 // to reflect the available space for the fixed items
246 nsHTMLReflowState
reflowState(aReflowState
);
248 if (reflowState
.AvailableBSize() == NS_UNCONSTRAINEDSIZE
) {
249 // We have an intrinsic-height document with abs-pos/fixed-pos children.
250 // Set the available height and mComputedHeight to our chosen height.
251 reflowState
.AvailableBSize() = maxSize
.BSize(wm
);
252 // Not having border/padding simplifies things
253 NS_ASSERTION(reflowState
.ComputedPhysicalBorderPadding() == nsMargin(0,0,0,0),
254 "Viewports can't have border/padding");
255 reflowState
.SetComputedBSize(maxSize
.BSize(wm
));
258 nsRect rect
= AdjustReflowStateAsContainingBlock(&reflowState
);
260 // Just reflow all the fixed-pos frames.
261 GetAbsoluteContainingBlock()->Reflow(this, aPresContext
, reflowState
, aStatus
,
263 false, true, true, // XXX could be optimized
264 &aDesiredSize
.mOverflowAreas
);
267 // If we were dirty then do a repaint
268 if (GetStateBits() & NS_FRAME_IS_DIRTY
) {
272 // Clipping is handled by the document container (e.g., nsSubDocumentFrame),
273 // so we don't need to change our overflow areas.
274 bool overflowChanged
= FinishAndStoreOverflow(&aDesiredSize
);
275 if (overflowChanged
) {
276 // We may need to alert our container to get it to pick up the
278 nsSubDocumentFrame
* container
= static_cast<nsSubDocumentFrame
*>
279 (nsLayoutUtils::GetCrossDocParentFrame(this));
280 if (container
&& !container
->ShouldClipSubdocument()) {
281 container
->PresContext()->PresShell()->
282 FrameNeedsReflow(container
, nsIPresShell::eResize
, NS_FRAME_IS_DIRTY
);
286 NS_FRAME_TRACE_REFLOW_OUT("ViewportFrame::Reflow", aStatus
);
287 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
291 ViewportFrame::GetType() const
293 return nsGkAtoms::viewportFrame
;
296 #ifdef DEBUG_FRAME_DUMP
298 ViewportFrame::GetFrameName(nsAString
& aResult
) const
300 return MakeFrameName(NS_LITERAL_STRING("Viewport"), aResult
);