1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : /*
39 : * rendering object that is the root of the frame tree, which contains
40 : * the document's scrollbars and contains fixed-positioned elements
41 : */
42 :
43 : #include "nsCOMPtr.h"
44 : #include "nsViewportFrame.h"
45 : #include "nsHTMLParts.h"
46 : #include "nsGkAtoms.h"
47 : #include "nsIScrollableFrame.h"
48 : #include "nsDisplayList.h"
49 : #include "FrameLayerBuilder.h"
50 : #include "nsSubDocumentFrame.h"
51 : #include "nsAbsoluteContainingBlock.h"
52 :
53 : using namespace mozilla;
54 :
55 : nsIFrame*
56 0 : NS_NewViewportFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
57 : {
58 0 : return new (aPresShell) ViewportFrame(aContext);
59 : }
60 :
61 0 : NS_IMPL_FRAMEARENA_HELPERS(ViewportFrame)
62 :
63 : NS_IMETHODIMP
64 0 : ViewportFrame::Init(nsIContent* aContent,
65 : nsIFrame* aParent,
66 : nsIFrame* aPrevInFlow)
67 : {
68 0 : return Super::Init(aContent, aParent, aPrevInFlow);
69 : }
70 :
71 : void
72 0 : ViewportFrame::DestroyFrom(nsIFrame* aDestructRoot)
73 : {
74 0 : DestroyAbsoluteFrames(aDestructRoot);
75 0 : nsContainerFrame::DestroyFrom(aDestructRoot);
76 0 : }
77 :
78 : NS_IMETHODIMP
79 0 : ViewportFrame::SetInitialChildList(ChildListID aListID,
80 : nsFrameList& aChildList)
81 : {
82 : // See which child list to add the frames to
83 : #ifdef NS_DEBUG
84 0 : nsFrame::VerifyDirtyBitSet(aChildList);
85 : #endif
86 0 : return nsContainerFrame::SetInitialChildList(aListID, aChildList);
87 : }
88 :
89 : NS_IMETHODIMP
90 0 : ViewportFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
91 : const nsRect& aDirtyRect,
92 : const nsDisplayListSet& aLists)
93 : {
94 0 : nsIFrame* kid = mFrames.FirstChild();
95 0 : if (!kid)
96 0 : return NS_OK;
97 :
98 : // make the kid's BorderBackground our own. This ensures that the canvas
99 : // frame's background becomes our own background and therefore appears
100 : // below negative z-index elements.
101 0 : return BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
102 : }
103 :
104 : NS_IMETHODIMP
105 0 : ViewportFrame::AppendFrames(ChildListID aListID,
106 : nsFrameList& aFrameList)
107 : {
108 0 : NS_ASSERTION(aListID == kPrincipalList ||
109 : aListID == GetAbsoluteListID(), "unexpected child list");
110 0 : NS_ASSERTION(aListID != GetAbsoluteListID() ||
111 : GetChildList(aListID).IsEmpty(), "Shouldn't have any kids!");
112 0 : return nsContainerFrame::AppendFrames(aListID, aFrameList);
113 : }
114 :
115 : NS_IMETHODIMP
116 0 : ViewportFrame::InsertFrames(ChildListID aListID,
117 : nsIFrame* aPrevFrame,
118 : nsFrameList& aFrameList)
119 : {
120 0 : NS_ASSERTION(aListID == kPrincipalList ||
121 : aListID == GetAbsoluteListID(), "unexpected child list");
122 0 : NS_ASSERTION(aListID != GetAbsoluteListID() ||
123 : GetChildList(aListID).IsEmpty(), "Shouldn't have any kids!");
124 0 : return nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
125 : }
126 :
127 : NS_IMETHODIMP
128 0 : ViewportFrame::RemoveFrame(ChildListID aListID,
129 : nsIFrame* aOldFrame)
130 : {
131 0 : NS_ASSERTION(aListID == kPrincipalList ||
132 : aListID == GetAbsoluteListID(), "unexpected child list");
133 0 : return nsContainerFrame::RemoveFrame(aListID, aOldFrame);
134 : }
135 :
136 : /* virtual */ nscoord
137 0 : ViewportFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
138 : {
139 : nscoord result;
140 0 : DISPLAY_MIN_WIDTH(this, result);
141 0 : if (mFrames.IsEmpty())
142 0 : result = 0;
143 : else
144 0 : result = mFrames.FirstChild()->GetMinWidth(aRenderingContext);
145 :
146 0 : return result;
147 : }
148 :
149 : /* virtual */ nscoord
150 0 : ViewportFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
151 : {
152 : nscoord result;
153 0 : DISPLAY_PREF_WIDTH(this, result);
154 0 : if (mFrames.IsEmpty())
155 0 : result = 0;
156 : else
157 0 : result = mFrames.FirstChild()->GetPrefWidth(aRenderingContext);
158 :
159 0 : return result;
160 : }
161 :
162 : nsPoint
163 0 : ViewportFrame::AdjustReflowStateForScrollbars(nsHTMLReflowState* aReflowState) const
164 : {
165 : // Calculate how much room is available for fixed frames. That means
166 : // determining if the viewport is scrollable and whether the vertical and/or
167 : // horizontal scrollbars are visible
168 :
169 : // Get our prinicpal child frame and see if we're scrollable
170 0 : nsIFrame* kidFrame = mFrames.FirstChild();
171 0 : nsIScrollableFrame *scrollingFrame = do_QueryFrame(kidFrame);
172 :
173 0 : if (scrollingFrame) {
174 0 : nsMargin scrollbars = scrollingFrame->GetActualScrollbarSizes();
175 0 : aReflowState->SetComputedWidth(aReflowState->ComputedWidth() -
176 0 : scrollbars.LeftRight());
177 0 : aReflowState->availableWidth -= scrollbars.LeftRight();
178 : aReflowState->SetComputedHeightWithoutResettingResizeFlags(
179 0 : aReflowState->ComputedHeight() - scrollbars.TopBottom());
180 0 : return nsPoint(scrollbars.left, scrollbars.top);
181 : }
182 0 : return nsPoint(0, 0);
183 : }
184 :
185 : NS_IMETHODIMP
186 0 : ViewportFrame::Reflow(nsPresContext* aPresContext,
187 : nsHTMLReflowMetrics& aDesiredSize,
188 : const nsHTMLReflowState& aReflowState,
189 : nsReflowStatus& aStatus)
190 : {
191 0 : DO_GLOBAL_REFLOW_COUNT("ViewportFrame");
192 0 : DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
193 0 : NS_FRAME_TRACE_REFLOW_IN("ViewportFrame::Reflow");
194 :
195 : // Initialize OUT parameters
196 0 : aStatus = NS_FRAME_COMPLETE;
197 :
198 : // Because |Reflow| sets mComputedHeight on the child to
199 : // availableHeight.
200 0 : AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
201 :
202 : // Set our size up front, since some parts of reflow depend on it
203 : // being already set. Note that the computed height may be
204 : // unconstrained; that's ok. Consumers should watch out for that.
205 0 : SetSize(nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight()));
206 :
207 : // Reflow the main content first so that the placeholders of the
208 : // fixed-position frames will be in the right places on an initial
209 : // reflow.
210 0 : nscoord kidHeight = 0;
211 :
212 0 : nsresult rv = NS_OK;
213 :
214 0 : if (mFrames.NotEmpty()) {
215 : // Deal with a non-incremental reflow or an incremental reflow
216 : // targeted at our one-and-only principal child frame.
217 0 : if (aReflowState.ShouldReflowAllKids() ||
218 : aReflowState.mFlags.mVResize ||
219 0 : NS_SUBTREE_DIRTY(mFrames.FirstChild())) {
220 : // Reflow our one-and-only principal child frame
221 0 : nsIFrame* kidFrame = mFrames.FirstChild();
222 0 : nsHTMLReflowMetrics kidDesiredSize;
223 : nsSize availableSpace(aReflowState.availableWidth,
224 0 : aReflowState.availableHeight);
225 : nsHTMLReflowState kidReflowState(aPresContext, aReflowState,
226 0 : kidFrame, availableSpace);
227 :
228 : // Reflow the frame
229 0 : kidReflowState.SetComputedHeight(aReflowState.ComputedHeight());
230 : rv = ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState,
231 0 : 0, 0, 0, aStatus);
232 0 : kidHeight = kidDesiredSize.height;
233 :
234 0 : FinishReflowChild(kidFrame, aPresContext, nsnull, kidDesiredSize, 0, 0, 0);
235 : } else {
236 0 : kidHeight = mFrames.FirstChild()->GetSize().height;
237 : }
238 : }
239 :
240 0 : NS_ASSERTION(aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE,
241 : "shouldn't happen anymore");
242 :
243 : // Return the max size as our desired size
244 0 : aDesiredSize.width = aReflowState.availableWidth;
245 : // Being flowed initially at an unconstrained height means we should
246 : // return our child's intrinsic size.
247 0 : aDesiredSize.height = aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE
248 : ? aReflowState.ComputedHeight()
249 0 : : kidHeight;
250 0 : aDesiredSize.SetOverflowAreasToDesiredBounds();
251 :
252 0 : if (mFrames.NotEmpty()) {
253 0 : ConsiderChildOverflow(aDesiredSize.mOverflowAreas, mFrames.FirstChild());
254 : }
255 :
256 : // Make a copy of the reflow state and change the computed width and height
257 : // to reflect the available space for the fixed items
258 0 : nsHTMLReflowState reflowState(aReflowState);
259 0 : nsPoint offset = AdjustReflowStateForScrollbars(&reflowState);
260 :
261 0 : if (IsAbsoluteContainer()) {
262 0 : NS_ASSERTION(GetAbsoluteContainingBlock()->GetChildList().IsEmpty() ||
263 : (offset.x == 0 && offset.y == 0),
264 : "We don't handle correct positioning of fixed frames with "
265 : "scrollbars in odd positions");
266 :
267 : // Just reflow all the fixed-pos frames.
268 : rv = GetAbsoluteContainingBlock()->Reflow(this, aPresContext, reflowState, aStatus,
269 : reflowState.ComputedWidth(),
270 : reflowState.ComputedHeight(),
271 : false, true, true, // XXX could be optimized
272 0 : &aDesiredSize.mOverflowAreas);
273 : }
274 :
275 : // If we were dirty then do a repaint
276 0 : if (GetStateBits() & NS_FRAME_IS_DIRTY) {
277 0 : nsRect damageRect(0, 0, aDesiredSize.width, aDesiredSize.height);
278 0 : Invalidate(damageRect);
279 : }
280 :
281 : // Clipping is handled by the document container (e.g., nsSubDocumentFrame),
282 : // so we don't need to change our overflow areas.
283 0 : bool overflowChanged = FinishAndStoreOverflow(&aDesiredSize);
284 0 : if (overflowChanged) {
285 : // We may need to alert our container to get it to pick up the
286 : // overflow change.
287 : nsSubDocumentFrame* container = static_cast<nsSubDocumentFrame*>
288 0 : (nsLayoutUtils::GetCrossDocParentFrame(this));
289 0 : if (container && !container->ShouldClipSubdocument()) {
290 0 : container->PresContext()->PresShell()->
291 0 : FrameNeedsReflow(container, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
292 : }
293 : }
294 :
295 0 : NS_FRAME_TRACE_REFLOW_OUT("ViewportFrame::Reflow", aStatus);
296 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
297 0 : return rv;
298 : }
299 :
300 : nsIAtom*
301 0 : ViewportFrame::GetType() const
302 : {
303 0 : return nsGkAtoms::viewportFrame;
304 : }
305 :
306 : void
307 0 : ViewportFrame::InvalidateInternal(const nsRect& aDamageRect,
308 : nscoord aX, nscoord aY, nsIFrame* aForChild,
309 : PRUint32 aFlags)
310 : {
311 0 : nsRect r = aDamageRect + nsPoint(aX, aY);
312 0 : nsPresContext* presContext = PresContext();
313 0 : presContext->NotifyInvalidation(r, aFlags);
314 :
315 0 : if ((mState & NS_FRAME_HAS_CONTAINER_LAYER) &&
316 0 : !(aFlags & INVALIDATE_NO_THEBES_LAYERS)) {
317 0 : FrameLayerBuilder::InvalidateThebesLayerContents(this, r);
318 : // Don't need to invalidate any more Thebes layers
319 0 : aFlags |= INVALIDATE_NO_THEBES_LAYERS;
320 0 : if (aFlags & INVALIDATE_ONLY_THEBES_LAYERS) {
321 : return;
322 : }
323 : }
324 :
325 0 : nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(this);
326 0 : if (parent) {
327 0 : if (!presContext->PresShell()->IsActive())
328 : return;
329 0 : nsPoint pt = -parent->GetOffsetToCrossDoc(this);
330 0 : PRInt32 ourAPD = presContext->AppUnitsPerDevPixel();
331 0 : PRInt32 parentAPD = parent->PresContext()->AppUnitsPerDevPixel();
332 0 : r = r.ConvertAppUnitsRoundOut(ourAPD, parentAPD);
333 : parent->InvalidateInternal(r, pt.x, pt.y, this,
334 0 : aFlags | INVALIDATE_CROSS_DOC);
335 : return;
336 : }
337 0 : InvalidateRoot(r, aFlags);
338 : }
339 :
340 : #ifdef DEBUG
341 : NS_IMETHODIMP
342 0 : ViewportFrame::GetFrameName(nsAString& aResult) const
343 : {
344 0 : return MakeFrameName(NS_LITERAL_STRING("Viewport"), aResult);
345 : }
346 : #endif
|