Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | /* |
michael@0 | 7 | * rendering object that is the root of the frame tree, which contains |
michael@0 | 8 | * the document's scrollbars and contains fixed-positioned elements |
michael@0 | 9 | */ |
michael@0 | 10 | |
michael@0 | 11 | #include "nsViewportFrame.h" |
michael@0 | 12 | #include "nsGkAtoms.h" |
michael@0 | 13 | #include "nsIScrollableFrame.h" |
michael@0 | 14 | #include "nsSubDocumentFrame.h" |
michael@0 | 15 | #include "nsAbsoluteContainingBlock.h" |
michael@0 | 16 | #include "GeckoProfiler.h" |
michael@0 | 17 | |
michael@0 | 18 | using namespace mozilla; |
michael@0 | 19 | |
michael@0 | 20 | nsIFrame* |
michael@0 | 21 | NS_NewViewportFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
michael@0 | 22 | { |
michael@0 | 23 | return new (aPresShell) ViewportFrame(aContext); |
michael@0 | 24 | } |
michael@0 | 25 | |
michael@0 | 26 | NS_IMPL_FRAMEARENA_HELPERS(ViewportFrame) |
michael@0 | 27 | NS_QUERYFRAME_HEAD(ViewportFrame) |
michael@0 | 28 | NS_QUERYFRAME_ENTRY(ViewportFrame) |
michael@0 | 29 | NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) |
michael@0 | 30 | |
michael@0 | 31 | void |
michael@0 | 32 | ViewportFrame::Init(nsIContent* aContent, |
michael@0 | 33 | nsIFrame* aParent, |
michael@0 | 34 | nsIFrame* aPrevInFlow) |
michael@0 | 35 | { |
michael@0 | 36 | Super::Init(aContent, aParent, aPrevInFlow); |
michael@0 | 37 | |
michael@0 | 38 | nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(this); |
michael@0 | 39 | if (parent) { |
michael@0 | 40 | nsFrameState state = parent->GetStateBits(); |
michael@0 | 41 | |
michael@0 | 42 | mState |= state & (NS_FRAME_IN_POPUP); |
michael@0 | 43 | } |
michael@0 | 44 | } |
michael@0 | 45 | |
michael@0 | 46 | nsresult |
michael@0 | 47 | ViewportFrame::SetInitialChildList(ChildListID aListID, |
michael@0 | 48 | nsFrameList& aChildList) |
michael@0 | 49 | { |
michael@0 | 50 | // See which child list to add the frames to |
michael@0 | 51 | #ifdef DEBUG |
michael@0 | 52 | nsFrame::VerifyDirtyBitSet(aChildList); |
michael@0 | 53 | #endif |
michael@0 | 54 | return nsContainerFrame::SetInitialChildList(aListID, aChildList); |
michael@0 | 55 | } |
michael@0 | 56 | |
michael@0 | 57 | void |
michael@0 | 58 | ViewportFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
michael@0 | 59 | const nsRect& aDirtyRect, |
michael@0 | 60 | const nsDisplayListSet& aLists) |
michael@0 | 61 | { |
michael@0 | 62 | PROFILER_LABEL("ViewportFrame", "BuildDisplayList"); |
michael@0 | 63 | nsIFrame* kid = mFrames.FirstChild(); |
michael@0 | 64 | if (!kid) |
michael@0 | 65 | return; |
michael@0 | 66 | |
michael@0 | 67 | // make the kid's BorderBackground our own. This ensures that the canvas |
michael@0 | 68 | // frame's background becomes our own background and therefore appears |
michael@0 | 69 | // below negative z-index elements. |
michael@0 | 70 | BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); |
michael@0 | 71 | } |
michael@0 | 72 | |
michael@0 | 73 | nsresult |
michael@0 | 74 | ViewportFrame::AppendFrames(ChildListID aListID, |
michael@0 | 75 | nsFrameList& aFrameList) |
michael@0 | 76 | { |
michael@0 | 77 | NS_ASSERTION(aListID == kPrincipalList || |
michael@0 | 78 | aListID == GetAbsoluteListID(), "unexpected child list"); |
michael@0 | 79 | NS_ASSERTION(aListID != GetAbsoluteListID() || |
michael@0 | 80 | GetChildList(aListID).IsEmpty(), "Shouldn't have any kids!"); |
michael@0 | 81 | return nsContainerFrame::AppendFrames(aListID, aFrameList); |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | nsresult |
michael@0 | 85 | ViewportFrame::InsertFrames(ChildListID aListID, |
michael@0 | 86 | nsIFrame* aPrevFrame, |
michael@0 | 87 | nsFrameList& aFrameList) |
michael@0 | 88 | { |
michael@0 | 89 | NS_ASSERTION(aListID == kPrincipalList || |
michael@0 | 90 | aListID == GetAbsoluteListID(), "unexpected child list"); |
michael@0 | 91 | NS_ASSERTION(aListID != GetAbsoluteListID() || |
michael@0 | 92 | GetChildList(aListID).IsEmpty(), "Shouldn't have any kids!"); |
michael@0 | 93 | return nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList); |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | nsresult |
michael@0 | 97 | ViewportFrame::RemoveFrame(ChildListID aListID, |
michael@0 | 98 | nsIFrame* aOldFrame) |
michael@0 | 99 | { |
michael@0 | 100 | NS_ASSERTION(aListID == kPrincipalList || |
michael@0 | 101 | aListID == GetAbsoluteListID(), "unexpected child list"); |
michael@0 | 102 | return nsContainerFrame::RemoveFrame(aListID, aOldFrame); |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | /* virtual */ nscoord |
michael@0 | 106 | ViewportFrame::GetMinWidth(nsRenderingContext *aRenderingContext) |
michael@0 | 107 | { |
michael@0 | 108 | nscoord result; |
michael@0 | 109 | DISPLAY_MIN_WIDTH(this, result); |
michael@0 | 110 | if (mFrames.IsEmpty()) |
michael@0 | 111 | result = 0; |
michael@0 | 112 | else |
michael@0 | 113 | result = mFrames.FirstChild()->GetMinWidth(aRenderingContext); |
michael@0 | 114 | |
michael@0 | 115 | return result; |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | /* virtual */ nscoord |
michael@0 | 119 | ViewportFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) |
michael@0 | 120 | { |
michael@0 | 121 | nscoord result; |
michael@0 | 122 | DISPLAY_PREF_WIDTH(this, result); |
michael@0 | 123 | if (mFrames.IsEmpty()) |
michael@0 | 124 | result = 0; |
michael@0 | 125 | else |
michael@0 | 126 | result = mFrames.FirstChild()->GetPrefWidth(aRenderingContext); |
michael@0 | 127 | |
michael@0 | 128 | return result; |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | nsPoint |
michael@0 | 132 | ViewportFrame::AdjustReflowStateForScrollbars(nsHTMLReflowState* aReflowState) const |
michael@0 | 133 | { |
michael@0 | 134 | // Get our prinicpal child frame and see if we're scrollable |
michael@0 | 135 | nsIFrame* kidFrame = mFrames.FirstChild(); |
michael@0 | 136 | nsIScrollableFrame* scrollingFrame = do_QueryFrame(kidFrame); |
michael@0 | 137 | |
michael@0 | 138 | if (scrollingFrame) { |
michael@0 | 139 | nsMargin scrollbars = scrollingFrame->GetActualScrollbarSizes(); |
michael@0 | 140 | aReflowState->SetComputedWidth(aReflowState->ComputedWidth() - |
michael@0 | 141 | scrollbars.LeftRight()); |
michael@0 | 142 | aReflowState->AvailableWidth() -= scrollbars.LeftRight(); |
michael@0 | 143 | aReflowState->SetComputedHeightWithoutResettingResizeFlags( |
michael@0 | 144 | aReflowState->ComputedHeight() - scrollbars.TopBottom()); |
michael@0 | 145 | return nsPoint(scrollbars.left, scrollbars.top); |
michael@0 | 146 | } |
michael@0 | 147 | return nsPoint(0, 0); |
michael@0 | 148 | } |
michael@0 | 149 | |
michael@0 | 150 | nsRect |
michael@0 | 151 | ViewportFrame::AdjustReflowStateAsContainingBlock(nsHTMLReflowState* aReflowState) const |
michael@0 | 152 | { |
michael@0 | 153 | #ifdef DEBUG |
michael@0 | 154 | nsPoint offset = |
michael@0 | 155 | #endif |
michael@0 | 156 | AdjustReflowStateForScrollbars(aReflowState); |
michael@0 | 157 | |
michael@0 | 158 | NS_ASSERTION(GetAbsoluteContainingBlock()->GetChildList().IsEmpty() || |
michael@0 | 159 | (offset.x == 0 && offset.y == 0), |
michael@0 | 160 | "We don't handle correct positioning of fixed frames with " |
michael@0 | 161 | "scrollbars in odd positions"); |
michael@0 | 162 | |
michael@0 | 163 | // If a scroll position clamping scroll-port size has been set, layout |
michael@0 | 164 | // fixed position elements to this size instead of the computed size. |
michael@0 | 165 | nsRect rect(0, 0, aReflowState->ComputedWidth(), aReflowState->ComputedHeight()); |
michael@0 | 166 | nsIPresShell* ps = PresContext()->PresShell(); |
michael@0 | 167 | if (ps->IsScrollPositionClampingScrollPortSizeSet()) { |
michael@0 | 168 | rect.SizeTo(ps->GetScrollPositionClampingScrollPortSize()); |
michael@0 | 169 | } |
michael@0 | 170 | |
michael@0 | 171 | // Make sure content document fixed-position margins are respected. |
michael@0 | 172 | rect.Deflate(ps->GetContentDocumentFixedPositionMargins()); |
michael@0 | 173 | return rect; |
michael@0 | 174 | } |
michael@0 | 175 | |
michael@0 | 176 | nsresult |
michael@0 | 177 | ViewportFrame::Reflow(nsPresContext* aPresContext, |
michael@0 | 178 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 179 | const nsHTMLReflowState& aReflowState, |
michael@0 | 180 | nsReflowStatus& aStatus) |
michael@0 | 181 | { |
michael@0 | 182 | DO_GLOBAL_REFLOW_COUNT("ViewportFrame"); |
michael@0 | 183 | DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); |
michael@0 | 184 | NS_FRAME_TRACE_REFLOW_IN("ViewportFrame::Reflow"); |
michael@0 | 185 | |
michael@0 | 186 | // Initialize OUT parameters |
michael@0 | 187 | aStatus = NS_FRAME_COMPLETE; |
michael@0 | 188 | |
michael@0 | 189 | // Because |Reflow| sets mComputedHeight on the child to |
michael@0 | 190 | // availableHeight. |
michael@0 | 191 | AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); |
michael@0 | 192 | |
michael@0 | 193 | // Set our size up front, since some parts of reflow depend on it |
michael@0 | 194 | // being already set. Note that the computed height may be |
michael@0 | 195 | // unconstrained; that's ok. Consumers should watch out for that. |
michael@0 | 196 | SetSize(nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); |
michael@0 | 197 | |
michael@0 | 198 | // Reflow the main content first so that the placeholders of the |
michael@0 | 199 | // fixed-position frames will be in the right places on an initial |
michael@0 | 200 | // reflow. |
michael@0 | 201 | nscoord kidHeight = 0; |
michael@0 | 202 | |
michael@0 | 203 | nsresult rv = NS_OK; |
michael@0 | 204 | |
michael@0 | 205 | if (mFrames.NotEmpty()) { |
michael@0 | 206 | // Deal with a non-incremental reflow or an incremental reflow |
michael@0 | 207 | // targeted at our one-and-only principal child frame. |
michael@0 | 208 | if (aReflowState.ShouldReflowAllKids() || |
michael@0 | 209 | aReflowState.mFlags.mVResize || |
michael@0 | 210 | NS_SUBTREE_DIRTY(mFrames.FirstChild())) { |
michael@0 | 211 | // Reflow our one-and-only principal child frame |
michael@0 | 212 | nsIFrame* kidFrame = mFrames.FirstChild(); |
michael@0 | 213 | nsHTMLReflowMetrics kidDesiredSize(aReflowState); |
michael@0 | 214 | nsSize availableSpace(aReflowState.AvailableWidth(), |
michael@0 | 215 | aReflowState.AvailableHeight()); |
michael@0 | 216 | nsHTMLReflowState kidReflowState(aPresContext, aReflowState, |
michael@0 | 217 | kidFrame, availableSpace); |
michael@0 | 218 | |
michael@0 | 219 | // Reflow the frame |
michael@0 | 220 | kidReflowState.SetComputedHeight(aReflowState.ComputedHeight()); |
michael@0 | 221 | rv = ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState, |
michael@0 | 222 | 0, 0, 0, aStatus); |
michael@0 | 223 | kidHeight = kidDesiredSize.Height(); |
michael@0 | 224 | |
michael@0 | 225 | FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, nullptr, 0, 0, 0); |
michael@0 | 226 | } else { |
michael@0 | 227 | kidHeight = mFrames.FirstChild()->GetSize().height; |
michael@0 | 228 | } |
michael@0 | 229 | } |
michael@0 | 230 | |
michael@0 | 231 | NS_ASSERTION(aReflowState.AvailableWidth() != NS_UNCONSTRAINEDSIZE, |
michael@0 | 232 | "shouldn't happen anymore"); |
michael@0 | 233 | |
michael@0 | 234 | // Return the max size as our desired size |
michael@0 | 235 | aDesiredSize.Width() = aReflowState.AvailableWidth(); |
michael@0 | 236 | // Being flowed initially at an unconstrained height means we should |
michael@0 | 237 | // return our child's intrinsic size. |
michael@0 | 238 | aDesiredSize.Height() = aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE |
michael@0 | 239 | ? aReflowState.ComputedHeight() |
michael@0 | 240 | : kidHeight; |
michael@0 | 241 | aDesiredSize.SetOverflowAreasToDesiredBounds(); |
michael@0 | 242 | |
michael@0 | 243 | if (mFrames.NotEmpty()) { |
michael@0 | 244 | ConsiderChildOverflow(aDesiredSize.mOverflowAreas, mFrames.FirstChild()); |
michael@0 | 245 | } |
michael@0 | 246 | |
michael@0 | 247 | if (IsAbsoluteContainer()) { |
michael@0 | 248 | // Make a copy of the reflow state and change the computed width and height |
michael@0 | 249 | // to reflect the available space for the fixed items |
michael@0 | 250 | nsHTMLReflowState reflowState(aReflowState); |
michael@0 | 251 | |
michael@0 | 252 | if (reflowState.AvailableHeight() == NS_UNCONSTRAINEDSIZE) { |
michael@0 | 253 | // We have an intrinsic-height document with abs-pos/fixed-pos children. |
michael@0 | 254 | // Set the available height and mComputedHeight to our chosen height. |
michael@0 | 255 | reflowState.AvailableHeight() = aDesiredSize.Height(); |
michael@0 | 256 | // Not having border/padding simplifies things |
michael@0 | 257 | NS_ASSERTION(reflowState.ComputedPhysicalBorderPadding() == nsMargin(0,0,0,0), |
michael@0 | 258 | "Viewports can't have border/padding"); |
michael@0 | 259 | reflowState.SetComputedHeight(aDesiredSize.Height()); |
michael@0 | 260 | } |
michael@0 | 261 | |
michael@0 | 262 | nsRect rect = AdjustReflowStateAsContainingBlock(&reflowState); |
michael@0 | 263 | |
michael@0 | 264 | // Just reflow all the fixed-pos frames. |
michael@0 | 265 | rv = GetAbsoluteContainingBlock()->Reflow(this, aPresContext, reflowState, aStatus, |
michael@0 | 266 | rect, |
michael@0 | 267 | false, true, true, // XXX could be optimized |
michael@0 | 268 | &aDesiredSize.mOverflowAreas); |
michael@0 | 269 | } |
michael@0 | 270 | |
michael@0 | 271 | // If we were dirty then do a repaint |
michael@0 | 272 | if (GetStateBits() & NS_FRAME_IS_DIRTY) { |
michael@0 | 273 | InvalidateFrame(); |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | // Clipping is handled by the document container (e.g., nsSubDocumentFrame), |
michael@0 | 277 | // so we don't need to change our overflow areas. |
michael@0 | 278 | bool overflowChanged = FinishAndStoreOverflow(&aDesiredSize); |
michael@0 | 279 | if (overflowChanged) { |
michael@0 | 280 | // We may need to alert our container to get it to pick up the |
michael@0 | 281 | // overflow change. |
michael@0 | 282 | nsSubDocumentFrame* container = static_cast<nsSubDocumentFrame*> |
michael@0 | 283 | (nsLayoutUtils::GetCrossDocParentFrame(this)); |
michael@0 | 284 | if (container && !container->ShouldClipSubdocument()) { |
michael@0 | 285 | container->PresContext()->PresShell()-> |
michael@0 | 286 | FrameNeedsReflow(container, nsIPresShell::eResize, NS_FRAME_IS_DIRTY); |
michael@0 | 287 | } |
michael@0 | 288 | } |
michael@0 | 289 | |
michael@0 | 290 | NS_FRAME_TRACE_REFLOW_OUT("ViewportFrame::Reflow", aStatus); |
michael@0 | 291 | NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); |
michael@0 | 292 | return rv; |
michael@0 | 293 | } |
michael@0 | 294 | |
michael@0 | 295 | nsIAtom* |
michael@0 | 296 | ViewportFrame::GetType() const |
michael@0 | 297 | { |
michael@0 | 298 | return nsGkAtoms::viewportFrame; |
michael@0 | 299 | } |
michael@0 | 300 | |
michael@0 | 301 | #ifdef DEBUG_FRAME_DUMP |
michael@0 | 302 | nsresult |
michael@0 | 303 | ViewportFrame::GetFrameName(nsAString& aResult) const |
michael@0 | 304 | { |
michael@0 | 305 | return MakeFrameName(NS_LITERAL_STRING("Viewport"), aResult); |
michael@0 | 306 | } |
michael@0 | 307 | #endif |