1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsViewportFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,307 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * rendering object that is the root of the frame tree, which contains 1.11 + * the document's scrollbars and contains fixed-positioned elements 1.12 + */ 1.13 + 1.14 +#include "nsViewportFrame.h" 1.15 +#include "nsGkAtoms.h" 1.16 +#include "nsIScrollableFrame.h" 1.17 +#include "nsSubDocumentFrame.h" 1.18 +#include "nsAbsoluteContainingBlock.h" 1.19 +#include "GeckoProfiler.h" 1.20 + 1.21 +using namespace mozilla; 1.22 + 1.23 +nsIFrame* 1.24 +NS_NewViewportFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.25 +{ 1.26 + return new (aPresShell) ViewportFrame(aContext); 1.27 +} 1.28 + 1.29 +NS_IMPL_FRAMEARENA_HELPERS(ViewportFrame) 1.30 +NS_QUERYFRAME_HEAD(ViewportFrame) 1.31 + NS_QUERYFRAME_ENTRY(ViewportFrame) 1.32 +NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) 1.33 + 1.34 +void 1.35 +ViewportFrame::Init(nsIContent* aContent, 1.36 + nsIFrame* aParent, 1.37 + nsIFrame* aPrevInFlow) 1.38 +{ 1.39 + Super::Init(aContent, aParent, aPrevInFlow); 1.40 + 1.41 + nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(this); 1.42 + if (parent) { 1.43 + nsFrameState state = parent->GetStateBits(); 1.44 + 1.45 + mState |= state & (NS_FRAME_IN_POPUP); 1.46 + } 1.47 +} 1.48 + 1.49 +nsresult 1.50 +ViewportFrame::SetInitialChildList(ChildListID aListID, 1.51 + nsFrameList& aChildList) 1.52 +{ 1.53 + // See which child list to add the frames to 1.54 +#ifdef DEBUG 1.55 + nsFrame::VerifyDirtyBitSet(aChildList); 1.56 +#endif 1.57 + return nsContainerFrame::SetInitialChildList(aListID, aChildList); 1.58 +} 1.59 + 1.60 +void 1.61 +ViewportFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.62 + const nsRect& aDirtyRect, 1.63 + const nsDisplayListSet& aLists) 1.64 +{ 1.65 + PROFILER_LABEL("ViewportFrame", "BuildDisplayList"); 1.66 + nsIFrame* kid = mFrames.FirstChild(); 1.67 + if (!kid) 1.68 + return; 1.69 + 1.70 + // make the kid's BorderBackground our own. This ensures that the canvas 1.71 + // frame's background becomes our own background and therefore appears 1.72 + // below negative z-index elements. 1.73 + BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); 1.74 +} 1.75 + 1.76 +nsresult 1.77 +ViewportFrame::AppendFrames(ChildListID aListID, 1.78 + nsFrameList& aFrameList) 1.79 +{ 1.80 + NS_ASSERTION(aListID == kPrincipalList || 1.81 + aListID == GetAbsoluteListID(), "unexpected child list"); 1.82 + NS_ASSERTION(aListID != GetAbsoluteListID() || 1.83 + GetChildList(aListID).IsEmpty(), "Shouldn't have any kids!"); 1.84 + return nsContainerFrame::AppendFrames(aListID, aFrameList); 1.85 +} 1.86 + 1.87 +nsresult 1.88 +ViewportFrame::InsertFrames(ChildListID aListID, 1.89 + nsIFrame* aPrevFrame, 1.90 + nsFrameList& aFrameList) 1.91 +{ 1.92 + NS_ASSERTION(aListID == kPrincipalList || 1.93 + aListID == GetAbsoluteListID(), "unexpected child list"); 1.94 + NS_ASSERTION(aListID != GetAbsoluteListID() || 1.95 + GetChildList(aListID).IsEmpty(), "Shouldn't have any kids!"); 1.96 + return nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList); 1.97 +} 1.98 + 1.99 +nsresult 1.100 +ViewportFrame::RemoveFrame(ChildListID aListID, 1.101 + nsIFrame* aOldFrame) 1.102 +{ 1.103 + NS_ASSERTION(aListID == kPrincipalList || 1.104 + aListID == GetAbsoluteListID(), "unexpected child list"); 1.105 + return nsContainerFrame::RemoveFrame(aListID, aOldFrame); 1.106 +} 1.107 + 1.108 +/* virtual */ nscoord 1.109 +ViewportFrame::GetMinWidth(nsRenderingContext *aRenderingContext) 1.110 +{ 1.111 + nscoord result; 1.112 + DISPLAY_MIN_WIDTH(this, result); 1.113 + if (mFrames.IsEmpty()) 1.114 + result = 0; 1.115 + else 1.116 + result = mFrames.FirstChild()->GetMinWidth(aRenderingContext); 1.117 + 1.118 + return result; 1.119 +} 1.120 + 1.121 +/* virtual */ nscoord 1.122 +ViewportFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) 1.123 +{ 1.124 + nscoord result; 1.125 + DISPLAY_PREF_WIDTH(this, result); 1.126 + if (mFrames.IsEmpty()) 1.127 + result = 0; 1.128 + else 1.129 + result = mFrames.FirstChild()->GetPrefWidth(aRenderingContext); 1.130 + 1.131 + return result; 1.132 +} 1.133 + 1.134 +nsPoint 1.135 +ViewportFrame::AdjustReflowStateForScrollbars(nsHTMLReflowState* aReflowState) const 1.136 +{ 1.137 + // Get our prinicpal child frame and see if we're scrollable 1.138 + nsIFrame* kidFrame = mFrames.FirstChild(); 1.139 + nsIScrollableFrame* scrollingFrame = do_QueryFrame(kidFrame); 1.140 + 1.141 + if (scrollingFrame) { 1.142 + nsMargin scrollbars = scrollingFrame->GetActualScrollbarSizes(); 1.143 + aReflowState->SetComputedWidth(aReflowState->ComputedWidth() - 1.144 + scrollbars.LeftRight()); 1.145 + aReflowState->AvailableWidth() -= scrollbars.LeftRight(); 1.146 + aReflowState->SetComputedHeightWithoutResettingResizeFlags( 1.147 + aReflowState->ComputedHeight() - scrollbars.TopBottom()); 1.148 + return nsPoint(scrollbars.left, scrollbars.top); 1.149 + } 1.150 + return nsPoint(0, 0); 1.151 +} 1.152 + 1.153 +nsRect 1.154 +ViewportFrame::AdjustReflowStateAsContainingBlock(nsHTMLReflowState* aReflowState) const 1.155 +{ 1.156 +#ifdef DEBUG 1.157 + nsPoint offset = 1.158 +#endif 1.159 + AdjustReflowStateForScrollbars(aReflowState); 1.160 + 1.161 + NS_ASSERTION(GetAbsoluteContainingBlock()->GetChildList().IsEmpty() || 1.162 + (offset.x == 0 && offset.y == 0), 1.163 + "We don't handle correct positioning of fixed frames with " 1.164 + "scrollbars in odd positions"); 1.165 + 1.166 + // If a scroll position clamping scroll-port size has been set, layout 1.167 + // fixed position elements to this size instead of the computed size. 1.168 + nsRect rect(0, 0, aReflowState->ComputedWidth(), aReflowState->ComputedHeight()); 1.169 + nsIPresShell* ps = PresContext()->PresShell(); 1.170 + if (ps->IsScrollPositionClampingScrollPortSizeSet()) { 1.171 + rect.SizeTo(ps->GetScrollPositionClampingScrollPortSize()); 1.172 + } 1.173 + 1.174 + // Make sure content document fixed-position margins are respected. 1.175 + rect.Deflate(ps->GetContentDocumentFixedPositionMargins()); 1.176 + return rect; 1.177 +} 1.178 + 1.179 +nsresult 1.180 +ViewportFrame::Reflow(nsPresContext* aPresContext, 1.181 + nsHTMLReflowMetrics& aDesiredSize, 1.182 + const nsHTMLReflowState& aReflowState, 1.183 + nsReflowStatus& aStatus) 1.184 +{ 1.185 + DO_GLOBAL_REFLOW_COUNT("ViewportFrame"); 1.186 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); 1.187 + NS_FRAME_TRACE_REFLOW_IN("ViewportFrame::Reflow"); 1.188 + 1.189 + // Initialize OUT parameters 1.190 + aStatus = NS_FRAME_COMPLETE; 1.191 + 1.192 + // Because |Reflow| sets mComputedHeight on the child to 1.193 + // availableHeight. 1.194 + AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); 1.195 + 1.196 + // Set our size up front, since some parts of reflow depend on it 1.197 + // being already set. Note that the computed height may be 1.198 + // unconstrained; that's ok. Consumers should watch out for that. 1.199 + SetSize(nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); 1.200 + 1.201 + // Reflow the main content first so that the placeholders of the 1.202 + // fixed-position frames will be in the right places on an initial 1.203 + // reflow. 1.204 + nscoord kidHeight = 0; 1.205 + 1.206 + nsresult rv = NS_OK; 1.207 + 1.208 + if (mFrames.NotEmpty()) { 1.209 + // Deal with a non-incremental reflow or an incremental reflow 1.210 + // targeted at our one-and-only principal child frame. 1.211 + if (aReflowState.ShouldReflowAllKids() || 1.212 + aReflowState.mFlags.mVResize || 1.213 + NS_SUBTREE_DIRTY(mFrames.FirstChild())) { 1.214 + // Reflow our one-and-only principal child frame 1.215 + nsIFrame* kidFrame = mFrames.FirstChild(); 1.216 + nsHTMLReflowMetrics kidDesiredSize(aReflowState); 1.217 + nsSize availableSpace(aReflowState.AvailableWidth(), 1.218 + aReflowState.AvailableHeight()); 1.219 + nsHTMLReflowState kidReflowState(aPresContext, aReflowState, 1.220 + kidFrame, availableSpace); 1.221 + 1.222 + // Reflow the frame 1.223 + kidReflowState.SetComputedHeight(aReflowState.ComputedHeight()); 1.224 + rv = ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState, 1.225 + 0, 0, 0, aStatus); 1.226 + kidHeight = kidDesiredSize.Height(); 1.227 + 1.228 + FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, nullptr, 0, 0, 0); 1.229 + } else { 1.230 + kidHeight = mFrames.FirstChild()->GetSize().height; 1.231 + } 1.232 + } 1.233 + 1.234 + NS_ASSERTION(aReflowState.AvailableWidth() != NS_UNCONSTRAINEDSIZE, 1.235 + "shouldn't happen anymore"); 1.236 + 1.237 + // Return the max size as our desired size 1.238 + aDesiredSize.Width() = aReflowState.AvailableWidth(); 1.239 + // Being flowed initially at an unconstrained height means we should 1.240 + // return our child's intrinsic size. 1.241 + aDesiredSize.Height() = aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE 1.242 + ? aReflowState.ComputedHeight() 1.243 + : kidHeight; 1.244 + aDesiredSize.SetOverflowAreasToDesiredBounds(); 1.245 + 1.246 + if (mFrames.NotEmpty()) { 1.247 + ConsiderChildOverflow(aDesiredSize.mOverflowAreas, mFrames.FirstChild()); 1.248 + } 1.249 + 1.250 + if (IsAbsoluteContainer()) { 1.251 + // Make a copy of the reflow state and change the computed width and height 1.252 + // to reflect the available space for the fixed items 1.253 + nsHTMLReflowState reflowState(aReflowState); 1.254 + 1.255 + if (reflowState.AvailableHeight() == NS_UNCONSTRAINEDSIZE) { 1.256 + // We have an intrinsic-height document with abs-pos/fixed-pos children. 1.257 + // Set the available height and mComputedHeight to our chosen height. 1.258 + reflowState.AvailableHeight() = aDesiredSize.Height(); 1.259 + // Not having border/padding simplifies things 1.260 + NS_ASSERTION(reflowState.ComputedPhysicalBorderPadding() == nsMargin(0,0,0,0), 1.261 + "Viewports can't have border/padding"); 1.262 + reflowState.SetComputedHeight(aDesiredSize.Height()); 1.263 + } 1.264 + 1.265 + nsRect rect = AdjustReflowStateAsContainingBlock(&reflowState); 1.266 + 1.267 + // Just reflow all the fixed-pos frames. 1.268 + rv = GetAbsoluteContainingBlock()->Reflow(this, aPresContext, reflowState, aStatus, 1.269 + rect, 1.270 + false, true, true, // XXX could be optimized 1.271 + &aDesiredSize.mOverflowAreas); 1.272 + } 1.273 + 1.274 + // If we were dirty then do a repaint 1.275 + if (GetStateBits() & NS_FRAME_IS_DIRTY) { 1.276 + InvalidateFrame(); 1.277 + } 1.278 + 1.279 + // Clipping is handled by the document container (e.g., nsSubDocumentFrame), 1.280 + // so we don't need to change our overflow areas. 1.281 + bool overflowChanged = FinishAndStoreOverflow(&aDesiredSize); 1.282 + if (overflowChanged) { 1.283 + // We may need to alert our container to get it to pick up the 1.284 + // overflow change. 1.285 + nsSubDocumentFrame* container = static_cast<nsSubDocumentFrame*> 1.286 + (nsLayoutUtils::GetCrossDocParentFrame(this)); 1.287 + if (container && !container->ShouldClipSubdocument()) { 1.288 + container->PresContext()->PresShell()-> 1.289 + FrameNeedsReflow(container, nsIPresShell::eResize, NS_FRAME_IS_DIRTY); 1.290 + } 1.291 + } 1.292 + 1.293 + NS_FRAME_TRACE_REFLOW_OUT("ViewportFrame::Reflow", aStatus); 1.294 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 1.295 + return rv; 1.296 +} 1.297 + 1.298 +nsIAtom* 1.299 +ViewportFrame::GetType() const 1.300 +{ 1.301 + return nsGkAtoms::viewportFrame; 1.302 +} 1.303 + 1.304 +#ifdef DEBUG_FRAME_DUMP 1.305 +nsresult 1.306 +ViewportFrame::GetFrameName(nsAString& aResult) const 1.307 +{ 1.308 + return MakeFrameName(NS_LITERAL_STRING("Viewport"), aResult); 1.309 +} 1.310 +#endif