Merge m-c to fx-team.
[gecko.git] / layout / generic / nsViewportFrame.cpp
blobc79f52db05dde8f0b26ec3372926ef49c783e238
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/. */
6 /*
7 * rendering object that is the root of the frame tree, which contains
8 * the document's scrollbars and contains fixed-positioned elements
9 */
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;
20 nsIFrame*
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)
31 void
32 ViewportFrame::Init(nsIContent* aContent,
33 nsIFrame* aParent,
34 nsIFrame* aPrevInFlow)
36 Super::Init(aContent, aParent, aPrevInFlow);
38 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(this);
39 if (parent) {
40 nsFrameState state = parent->GetStateBits();
42 mState |= state & (NS_FRAME_IN_POPUP);
46 NS_IMETHODIMP
47 ViewportFrame::SetInitialChildList(ChildListID aListID,
48 nsFrameList& aChildList)
50 // See which child list to add the frames to
51 #ifdef DEBUG
52 nsFrame::VerifyDirtyBitSet(aChildList);
53 #endif
54 return nsContainerFrame::SetInitialChildList(aListID, aChildList);
57 void
58 ViewportFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
59 const nsRect& aDirtyRect,
60 const nsDisplayListSet& aLists)
62 PROFILER_LABEL("ViewportFrame", "BuildDisplayList");
63 nsIFrame* kid = mFrames.FirstChild();
64 if (!kid)
65 return;
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);
73 NS_IMETHODIMP
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);
84 NS_IMETHODIMP
85 ViewportFrame::InsertFrames(ChildListID aListID,
86 nsIFrame* aPrevFrame,
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);
96 NS_IMETHODIMP
97 ViewportFrame::RemoveFrame(ChildListID aListID,
98 nsIFrame* aOldFrame)
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)
108 nscoord result;
109 DISPLAY_MIN_WIDTH(this, result);
110 if (mFrames.IsEmpty())
111 result = 0;
112 else
113 result = mFrames.FirstChild()->GetMinWidth(aRenderingContext);
115 return result;
118 /* virtual */ nscoord
119 ViewportFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
121 nscoord result;
122 DISPLAY_PREF_WIDTH(this, result);
123 if (mFrames.IsEmpty())
124 result = 0;
125 else
126 result = mFrames.FirstChild()->GetPrefWidth(aRenderingContext);
128 return result;
131 nsPoint
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);
150 nsRect
151 ViewportFrame::AdjustReflowStateAsContainingBlock(nsHTMLReflowState* aReflowState) const
153 #ifdef DEBUG
154 nsPoint offset =
155 #endif
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());
173 return rect;
176 NS_IMETHODIMP
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
190 // availableHeight.
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
200 // reflow.
201 nscoord kidHeight = 0;
203 nsresult rv = NS_OK;
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);
219 // Reflow the frame
220 kidReflowState.SetComputedHeight(aReflowState.ComputedHeight());
221 rv = ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState,
222 0, 0, 0, aStatus);
223 kidHeight = kidDesiredSize.height;
225 FinishReflowChild(kidFrame, aPresContext, nullptr, kidDesiredSize, 0, 0, 0);
226 } else {
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()
240 : kidHeight;
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,
266 rect,
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) {
273 InvalidateFrame();
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
281 // overflow change.
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);
292 return rv;
295 nsIAtom*
296 ViewportFrame::GetType() const
298 return nsGkAtoms::viewportFrame;
301 #ifdef DEBUG
302 NS_IMETHODIMP
303 ViewportFrame::GetFrameName(nsAString& aResult) const
305 return MakeFrameName(NS_LITERAL_STRING("Viewport"), aResult);
307 #endif