Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | // vim:cindent:ts=2:et:sw=2: |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /* state used in reflow of block frames */ |
michael@0 | 8 | |
michael@0 | 9 | #include "nsBlockReflowState.h" |
michael@0 | 10 | |
michael@0 | 11 | #include "mozilla/DebugOnly.h" |
michael@0 | 12 | |
michael@0 | 13 | #include "nsBlockFrame.h" |
michael@0 | 14 | #include "nsLineLayout.h" |
michael@0 | 15 | #include "nsPresContext.h" |
michael@0 | 16 | #include "nsIFrameInlines.h" |
michael@0 | 17 | #include "mozilla/AutoRestore.h" |
michael@0 | 18 | #include <algorithm> |
michael@0 | 19 | |
michael@0 | 20 | #ifdef DEBUG |
michael@0 | 21 | #include "nsBlockDebugFlags.h" |
michael@0 | 22 | #endif |
michael@0 | 23 | |
michael@0 | 24 | using namespace mozilla; |
michael@0 | 25 | using namespace mozilla::layout; |
michael@0 | 26 | |
michael@0 | 27 | nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, |
michael@0 | 28 | nsPresContext* aPresContext, |
michael@0 | 29 | nsBlockFrame* aFrame, |
michael@0 | 30 | bool aTopMarginRoot, |
michael@0 | 31 | bool aBottomMarginRoot, |
michael@0 | 32 | bool aBlockNeedsFloatManager, |
michael@0 | 33 | nscoord aConsumedHeight) |
michael@0 | 34 | : mBlock(aFrame), |
michael@0 | 35 | mPresContext(aPresContext), |
michael@0 | 36 | mReflowState(aReflowState), |
michael@0 | 37 | mPushedFloats(nullptr), |
michael@0 | 38 | mOverflowTracker(nullptr), |
michael@0 | 39 | mPrevBottomMargin(), |
michael@0 | 40 | mLineNumber(0), |
michael@0 | 41 | mFlags(0), |
michael@0 | 42 | mFloatBreakType(NS_STYLE_CLEAR_NONE), |
michael@0 | 43 | mConsumedHeight(aConsumedHeight) |
michael@0 | 44 | { |
michael@0 | 45 | SetFlag(BRS_ISFIRSTINFLOW, aFrame->GetPrevInFlow() == nullptr); |
michael@0 | 46 | SetFlag(BRS_ISOVERFLOWCONTAINER, |
michael@0 | 47 | IS_TRUE_OVERFLOW_CONTAINER(aFrame)); |
michael@0 | 48 | |
michael@0 | 49 | const nsMargin& borderPadding = BorderPadding(); |
michael@0 | 50 | mContainerWidth = aReflowState.ComputedWidth() + |
michael@0 | 51 | aReflowState.ComputedPhysicalBorderPadding().LeftRight(); |
michael@0 | 52 | |
michael@0 | 53 | if (aTopMarginRoot || 0 != aReflowState.ComputedPhysicalBorderPadding().top) { |
michael@0 | 54 | SetFlag(BRS_ISTOPMARGINROOT, true); |
michael@0 | 55 | } |
michael@0 | 56 | if (aBottomMarginRoot || 0 != aReflowState.ComputedPhysicalBorderPadding().bottom) { |
michael@0 | 57 | SetFlag(BRS_ISBOTTOMMARGINROOT, true); |
michael@0 | 58 | } |
michael@0 | 59 | if (GetFlag(BRS_ISTOPMARGINROOT)) { |
michael@0 | 60 | SetFlag(BRS_APPLYTOPMARGIN, true); |
michael@0 | 61 | } |
michael@0 | 62 | if (aBlockNeedsFloatManager) { |
michael@0 | 63 | SetFlag(BRS_FLOAT_MGR, true); |
michael@0 | 64 | } |
michael@0 | 65 | |
michael@0 | 66 | mFloatManager = aReflowState.mFloatManager; |
michael@0 | 67 | |
michael@0 | 68 | NS_ASSERTION(mFloatManager, |
michael@0 | 69 | "FloatManager should be set in nsBlockReflowState" ); |
michael@0 | 70 | if (mFloatManager) { |
michael@0 | 71 | // Save the coordinate system origin for later. |
michael@0 | 72 | mFloatManager->GetTranslation(mFloatManagerX, mFloatManagerY); |
michael@0 | 73 | mFloatManager->PushState(&mFloatManagerStateBefore); // never popped |
michael@0 | 74 | } |
michael@0 | 75 | |
michael@0 | 76 | mReflowStatus = NS_FRAME_COMPLETE; |
michael@0 | 77 | |
michael@0 | 78 | mNextInFlow = static_cast<nsBlockFrame*>(mBlock->GetNextInFlow()); |
michael@0 | 79 | |
michael@0 | 80 | NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowState.ComputedWidth(), |
michael@0 | 81 | "have unconstrained width; this should only result from " |
michael@0 | 82 | "very large sizes, not attempts at intrinsic width " |
michael@0 | 83 | "calculation"); |
michael@0 | 84 | mContentArea.width = aReflowState.ComputedWidth(); |
michael@0 | 85 | |
michael@0 | 86 | // Compute content area height. Unlike the width, if we have a |
michael@0 | 87 | // specified style height we ignore it since extra content is |
michael@0 | 88 | // managed by the "overflow" property. When we don't have a |
michael@0 | 89 | // specified style height then we may end up limiting our height if |
michael@0 | 90 | // the availableHeight is constrained (this situation occurs when we |
michael@0 | 91 | // are paginated). |
michael@0 | 92 | if (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight()) { |
michael@0 | 93 | // We are in a paginated situation. The bottom edge is just inside |
michael@0 | 94 | // the bottom border and padding. The content area height doesn't |
michael@0 | 95 | // include either border or padding edge. |
michael@0 | 96 | mBottomEdge = aReflowState.AvailableHeight() - borderPadding.bottom; |
michael@0 | 97 | mContentArea.height = std::max(0, mBottomEdge - borderPadding.top); |
michael@0 | 98 | } |
michael@0 | 99 | else { |
michael@0 | 100 | // When we are not in a paginated situation then we always use |
michael@0 | 101 | // an constrained height. |
michael@0 | 102 | SetFlag(BRS_UNCONSTRAINEDHEIGHT, true); |
michael@0 | 103 | mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE; |
michael@0 | 104 | } |
michael@0 | 105 | mContentArea.x = borderPadding.left; |
michael@0 | 106 | mY = mContentArea.y = borderPadding.top; |
michael@0 | 107 | |
michael@0 | 108 | mPrevChild = nullptr; |
michael@0 | 109 | mCurrentLine = aFrame->end_lines(); |
michael@0 | 110 | |
michael@0 | 111 | mMinLineHeight = aReflowState.CalcLineHeight(); |
michael@0 | 112 | } |
michael@0 | 113 | |
michael@0 | 114 | nscoord |
michael@0 | 115 | nsBlockReflowState::GetConsumedHeight() |
michael@0 | 116 | { |
michael@0 | 117 | if (mConsumedHeight == NS_INTRINSICSIZE) { |
michael@0 | 118 | mConsumedHeight = mBlock->GetConsumedHeight(); |
michael@0 | 119 | } |
michael@0 | 120 | |
michael@0 | 121 | return mConsumedHeight; |
michael@0 | 122 | } |
michael@0 | 123 | |
michael@0 | 124 | void |
michael@0 | 125 | nsBlockReflowState::ComputeReplacedBlockOffsetsForFloats(nsIFrame* aFrame, |
michael@0 | 126 | const nsRect& aFloatAvailableSpace, |
michael@0 | 127 | nscoord& aLeftResult, |
michael@0 | 128 | nscoord& aRightResult) |
michael@0 | 129 | { |
michael@0 | 130 | // The frame is clueless about the float manager and therefore we |
michael@0 | 131 | // only give it free space. An example is a table frame - the |
michael@0 | 132 | // tables do not flow around floats. |
michael@0 | 133 | // However, we can let its margins intersect floats. |
michael@0 | 134 | NS_ASSERTION(aFloatAvailableSpace.x >= mContentArea.x, "bad avail space rect x"); |
michael@0 | 135 | NS_ASSERTION(aFloatAvailableSpace.width == 0 || |
michael@0 | 136 | aFloatAvailableSpace.XMost() <= mContentArea.XMost(), |
michael@0 | 137 | "bad avail space rect width"); |
michael@0 | 138 | |
michael@0 | 139 | nscoord leftOffset, rightOffset; |
michael@0 | 140 | if (aFloatAvailableSpace.width == mContentArea.width) { |
michael@0 | 141 | // We don't need to compute margins when there are no floats around. |
michael@0 | 142 | leftOffset = 0; |
michael@0 | 143 | rightOffset = 0; |
michael@0 | 144 | } else { |
michael@0 | 145 | nsMargin frameMargin; |
michael@0 | 146 | nsCSSOffsetState os(aFrame, mReflowState.rendContext, mContentArea.width); |
michael@0 | 147 | frameMargin = os.ComputedPhysicalMargin(); |
michael@0 | 148 | |
michael@0 | 149 | nscoord leftFloatXOffset = aFloatAvailableSpace.x - mContentArea.x; |
michael@0 | 150 | leftOffset = std::max(leftFloatXOffset, frameMargin.left) - |
michael@0 | 151 | frameMargin.left; |
michael@0 | 152 | leftOffset = std::max(leftOffset, 0); // in case of negative margin |
michael@0 | 153 | nscoord rightFloatXOffset = |
michael@0 | 154 | mContentArea.XMost() - aFloatAvailableSpace.XMost(); |
michael@0 | 155 | rightOffset = std::max(rightFloatXOffset, frameMargin.right) - |
michael@0 | 156 | frameMargin.right; |
michael@0 | 157 | rightOffset = std::max(rightOffset, 0); // in case of negative margin |
michael@0 | 158 | } |
michael@0 | 159 | aLeftResult = leftOffset; |
michael@0 | 160 | aRightResult = rightOffset; |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | // Compute the amount of available space for reflowing a block frame |
michael@0 | 164 | // at the current Y coordinate. This method assumes that |
michael@0 | 165 | // GetAvailableSpace has already been called. |
michael@0 | 166 | void |
michael@0 | 167 | nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, |
michael@0 | 168 | const nsStyleDisplay* aDisplay, |
michael@0 | 169 | const nsFlowAreaRect& aFloatAvailableSpace, |
michael@0 | 170 | bool aBlockAvoidsFloats, |
michael@0 | 171 | nsRect& aResult) |
michael@0 | 172 | { |
michael@0 | 173 | #ifdef REALLY_NOISY_REFLOW |
michael@0 | 174 | printf("CBAS frame=%p has floats %d\n", |
michael@0 | 175 | aFrame, aFloatAvailableSpace.mHasFloats); |
michael@0 | 176 | #endif |
michael@0 | 177 | aResult.y = mY; |
michael@0 | 178 | aResult.height = GetFlag(BRS_UNCONSTRAINEDHEIGHT) |
michael@0 | 179 | ? NS_UNCONSTRAINEDSIZE |
michael@0 | 180 | : mReflowState.AvailableHeight() - mY; |
michael@0 | 181 | // mY might be greater than mBottomEdge if the block's top margin pushes |
michael@0 | 182 | // it off the page/column. Negative available height can confuse other code |
michael@0 | 183 | // and is nonsense in principle. |
michael@0 | 184 | |
michael@0 | 185 | // XXX Do we really want this condition to be this restrictive (i.e., |
michael@0 | 186 | // more restrictive than it used to be)? The |else| here is allowed |
michael@0 | 187 | // by the CSS spec, but only out of desperation given implementations, |
michael@0 | 188 | // and the behavior it leads to is quite undesirable (it can cause |
michael@0 | 189 | // things to become extremely narrow when they'd fit quite well a |
michael@0 | 190 | // little bit lower). Should the else be a quirk or something that |
michael@0 | 191 | // applies to a specific set of frame classes and no new ones? |
michael@0 | 192 | // If we did that, then for those frames where the condition below is |
michael@0 | 193 | // true but nsBlockFrame::BlockCanIntersectFloats is false, |
michael@0 | 194 | // nsBlockFrame::WidthToClearPastFloats would need to use the |
michael@0 | 195 | // shrink-wrap formula, max(MIN_WIDTH, min(avail width, PREF_WIDTH)) |
michael@0 | 196 | // rather than just using MIN_WIDTH. |
michael@0 | 197 | NS_ASSERTION(nsBlockFrame::BlockCanIntersectFloats(aFrame) == |
michael@0 | 198 | !aBlockAvoidsFloats, |
michael@0 | 199 | "unexpected replaced width"); |
michael@0 | 200 | if (!aBlockAvoidsFloats) { |
michael@0 | 201 | if (aFloatAvailableSpace.mHasFloats) { |
michael@0 | 202 | // Use the float-edge property to determine how the child block |
michael@0 | 203 | // will interact with the float. |
michael@0 | 204 | const nsStyleBorder* borderStyle = aFrame->StyleBorder(); |
michael@0 | 205 | switch (borderStyle->mFloatEdge) { |
michael@0 | 206 | default: |
michael@0 | 207 | case NS_STYLE_FLOAT_EDGE_CONTENT: // content and only content does runaround of floats |
michael@0 | 208 | // The child block will flow around the float. Therefore |
michael@0 | 209 | // give it all of the available space. |
michael@0 | 210 | aResult.x = mContentArea.x; |
michael@0 | 211 | aResult.width = mContentArea.width; |
michael@0 | 212 | break; |
michael@0 | 213 | case NS_STYLE_FLOAT_EDGE_MARGIN: |
michael@0 | 214 | { |
michael@0 | 215 | // The child block's margins should be placed adjacent to, |
michael@0 | 216 | // but not overlap the float. |
michael@0 | 217 | aResult.x = aFloatAvailableSpace.mRect.x; |
michael@0 | 218 | aResult.width = aFloatAvailableSpace.mRect.width; |
michael@0 | 219 | } |
michael@0 | 220 | break; |
michael@0 | 221 | } |
michael@0 | 222 | } |
michael@0 | 223 | else { |
michael@0 | 224 | // Since there are no floats present the float-edge property |
michael@0 | 225 | // doesn't matter therefore give the block element all of the |
michael@0 | 226 | // available space since it will flow around the float itself. |
michael@0 | 227 | aResult.x = mContentArea.x; |
michael@0 | 228 | aResult.width = mContentArea.width; |
michael@0 | 229 | } |
michael@0 | 230 | } |
michael@0 | 231 | else { |
michael@0 | 232 | nscoord leftOffset, rightOffset; |
michael@0 | 233 | ComputeReplacedBlockOffsetsForFloats(aFrame, aFloatAvailableSpace.mRect, |
michael@0 | 234 | leftOffset, rightOffset); |
michael@0 | 235 | aResult.x = mContentArea.x + leftOffset; |
michael@0 | 236 | aResult.width = mContentArea.width - leftOffset - rightOffset; |
michael@0 | 237 | } |
michael@0 | 238 | |
michael@0 | 239 | #ifdef REALLY_NOISY_REFLOW |
michael@0 | 240 | printf(" CBAS: result %d %d %d %d\n", aResult.x, aResult.y, aResult.width, aResult.height); |
michael@0 | 241 | #endif |
michael@0 | 242 | } |
michael@0 | 243 | |
michael@0 | 244 | nsFlowAreaRect |
michael@0 | 245 | nsBlockReflowState::GetFloatAvailableSpaceWithState( |
michael@0 | 246 | nscoord aY, |
michael@0 | 247 | nsFloatManager::SavedState *aState) const |
michael@0 | 248 | { |
michael@0 | 249 | #ifdef DEBUG |
michael@0 | 250 | // Verify that the caller setup the coordinate system properly |
michael@0 | 251 | nscoord wx, wy; |
michael@0 | 252 | mFloatManager->GetTranslation(wx, wy); |
michael@0 | 253 | NS_ASSERTION((wx == mFloatManagerX) && (wy == mFloatManagerY), |
michael@0 | 254 | "bad coord system"); |
michael@0 | 255 | #endif |
michael@0 | 256 | |
michael@0 | 257 | nscoord height = (mContentArea.height == nscoord_MAX) |
michael@0 | 258 | ? nscoord_MAX : std::max(mContentArea.YMost() - aY, 0); |
michael@0 | 259 | nsFlowAreaRect result = |
michael@0 | 260 | mFloatManager->GetFlowArea(aY, nsFloatManager::BAND_FROM_POINT, |
michael@0 | 261 | height, mContentArea, aState); |
michael@0 | 262 | // Keep the width >= 0 for compatibility with nsSpaceManager. |
michael@0 | 263 | if (result.mRect.width < 0) |
michael@0 | 264 | result.mRect.width = 0; |
michael@0 | 265 | |
michael@0 | 266 | #ifdef DEBUG |
michael@0 | 267 | if (nsBlockFrame::gNoisyReflow) { |
michael@0 | 268 | nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); |
michael@0 | 269 | printf("GetAvailableSpace: band=%d,%d,%d,%d hasfloats=%d\n", |
michael@0 | 270 | result.mRect.x, result.mRect.y, result.mRect.width, |
michael@0 | 271 | result.mRect.height, result.mHasFloats); |
michael@0 | 272 | } |
michael@0 | 273 | #endif |
michael@0 | 274 | return result; |
michael@0 | 275 | } |
michael@0 | 276 | |
michael@0 | 277 | nsFlowAreaRect |
michael@0 | 278 | nsBlockReflowState::GetFloatAvailableSpaceForHeight( |
michael@0 | 279 | nscoord aY, nscoord aHeight, |
michael@0 | 280 | nsFloatManager::SavedState *aState) const |
michael@0 | 281 | { |
michael@0 | 282 | #ifdef DEBUG |
michael@0 | 283 | // Verify that the caller setup the coordinate system properly |
michael@0 | 284 | nscoord wx, wy; |
michael@0 | 285 | mFloatManager->GetTranslation(wx, wy); |
michael@0 | 286 | NS_ASSERTION((wx == mFloatManagerX) && (wy == mFloatManagerY), |
michael@0 | 287 | "bad coord system"); |
michael@0 | 288 | #endif |
michael@0 | 289 | |
michael@0 | 290 | nsFlowAreaRect result = |
michael@0 | 291 | mFloatManager->GetFlowArea(aY, nsFloatManager::WIDTH_WITHIN_HEIGHT, |
michael@0 | 292 | aHeight, mContentArea, aState); |
michael@0 | 293 | // Keep the width >= 0 for compatibility with nsSpaceManager. |
michael@0 | 294 | if (result.mRect.width < 0) |
michael@0 | 295 | result.mRect.width = 0; |
michael@0 | 296 | |
michael@0 | 297 | #ifdef DEBUG |
michael@0 | 298 | if (nsBlockFrame::gNoisyReflow) { |
michael@0 | 299 | nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); |
michael@0 | 300 | printf("GetAvailableSpaceForHeight: space=%d,%d,%d,%d hasfloats=%d\n", |
michael@0 | 301 | result.mRect.x, result.mRect.y, result.mRect.width, |
michael@0 | 302 | result.mRect.height, result.mHasFloats); |
michael@0 | 303 | } |
michael@0 | 304 | #endif |
michael@0 | 305 | return result; |
michael@0 | 306 | } |
michael@0 | 307 | |
michael@0 | 308 | /* |
michael@0 | 309 | * Reconstruct the vertical margin before the line |aLine| in order to |
michael@0 | 310 | * do an incremental reflow that begins with |aLine| without reflowing |
michael@0 | 311 | * the line before it. |aLine| may point to the fencepost at the end of |
michael@0 | 312 | * the line list, and it is used this way since we (for now, anyway) |
michael@0 | 313 | * always need to recover margins at the end of a block. |
michael@0 | 314 | * |
michael@0 | 315 | * The reconstruction involves walking backward through the line list to |
michael@0 | 316 | * find any collapsed margins preceding the line that would have been in |
michael@0 | 317 | * the reflow state's |mPrevBottomMargin| when we reflowed that line in |
michael@0 | 318 | * a full reflow (under the rule in CSS2 that all adjacent vertical |
michael@0 | 319 | * margins of blocks collapse). |
michael@0 | 320 | */ |
michael@0 | 321 | void |
michael@0 | 322 | nsBlockReflowState::ReconstructMarginAbove(nsLineList::iterator aLine) |
michael@0 | 323 | { |
michael@0 | 324 | mPrevBottomMargin.Zero(); |
michael@0 | 325 | nsBlockFrame *block = mBlock; |
michael@0 | 326 | |
michael@0 | 327 | nsLineList::iterator firstLine = block->begin_lines(); |
michael@0 | 328 | for (;;) { |
michael@0 | 329 | --aLine; |
michael@0 | 330 | if (aLine->IsBlock()) { |
michael@0 | 331 | mPrevBottomMargin = aLine->GetCarriedOutBottomMargin(); |
michael@0 | 332 | break; |
michael@0 | 333 | } |
michael@0 | 334 | if (!aLine->IsEmpty()) { |
michael@0 | 335 | break; |
michael@0 | 336 | } |
michael@0 | 337 | if (aLine == firstLine) { |
michael@0 | 338 | // If the top margin was carried out (and thus already applied), |
michael@0 | 339 | // set it to zero. Either way, we're done. |
michael@0 | 340 | if (!GetFlag(BRS_ISTOPMARGINROOT)) { |
michael@0 | 341 | mPrevBottomMargin.Zero(); |
michael@0 | 342 | } |
michael@0 | 343 | break; |
michael@0 | 344 | } |
michael@0 | 345 | } |
michael@0 | 346 | } |
michael@0 | 347 | |
michael@0 | 348 | void |
michael@0 | 349 | nsBlockReflowState::SetupPushedFloatList() |
michael@0 | 350 | { |
michael@0 | 351 | NS_ABORT_IF_FALSE(!GetFlag(BRS_PROPTABLE_FLOATCLIST) == !mPushedFloats, |
michael@0 | 352 | "flag mismatch"); |
michael@0 | 353 | if (!GetFlag(BRS_PROPTABLE_FLOATCLIST)) { |
michael@0 | 354 | // If we're being re-Reflow'd without our next-in-flow having been |
michael@0 | 355 | // reflowed, some pushed floats from our previous reflow might |
michael@0 | 356 | // still be on our pushed floats list. However, that's |
michael@0 | 357 | // actually fine, since they'll all end up being stolen and |
michael@0 | 358 | // reordered into the correct order again. |
michael@0 | 359 | // (nsBlockFrame::ReflowDirtyLines ensures that any lines with |
michael@0 | 360 | // pushed floats are reflowed.) |
michael@0 | 361 | mPushedFloats = mBlock->EnsurePushedFloats(); |
michael@0 | 362 | SetFlag(BRS_PROPTABLE_FLOATCLIST, true); |
michael@0 | 363 | } |
michael@0 | 364 | } |
michael@0 | 365 | |
michael@0 | 366 | void |
michael@0 | 367 | nsBlockReflowState::AppendPushedFloat(nsIFrame* aFloatCont) |
michael@0 | 368 | { |
michael@0 | 369 | SetupPushedFloatList(); |
michael@0 | 370 | aFloatCont->AddStateBits(NS_FRAME_IS_PUSHED_FLOAT); |
michael@0 | 371 | mPushedFloats->AppendFrame(mBlock, aFloatCont); |
michael@0 | 372 | } |
michael@0 | 373 | |
michael@0 | 374 | /** |
michael@0 | 375 | * Restore information about floats into the float manager for an |
michael@0 | 376 | * incremental reflow, and simultaneously push the floats by |
michael@0 | 377 | * |aDeltaY|, which is the amount |aLine| was pushed relative to its |
michael@0 | 378 | * parent. The recovery of state is one of the things that makes |
michael@0 | 379 | * incremental reflow O(N^2) and this state should really be kept |
michael@0 | 380 | * around, attached to the frame tree. |
michael@0 | 381 | */ |
michael@0 | 382 | void |
michael@0 | 383 | nsBlockReflowState::RecoverFloats(nsLineList::iterator aLine, |
michael@0 | 384 | nscoord aDeltaY) |
michael@0 | 385 | { |
michael@0 | 386 | if (aLine->HasFloats()) { |
michael@0 | 387 | // Place the floats into the space-manager again. Also slide |
michael@0 | 388 | // them, just like the regular frames on the line. |
michael@0 | 389 | nsFloatCache* fc = aLine->GetFirstFloat(); |
michael@0 | 390 | while (fc) { |
michael@0 | 391 | nsIFrame* floatFrame = fc->mFloat; |
michael@0 | 392 | if (aDeltaY != 0) { |
michael@0 | 393 | floatFrame->MovePositionBy(nsPoint(0, aDeltaY)); |
michael@0 | 394 | nsContainerFrame::PositionFrameView(floatFrame); |
michael@0 | 395 | nsContainerFrame::PositionChildViews(floatFrame); |
michael@0 | 396 | } |
michael@0 | 397 | #ifdef DEBUG |
michael@0 | 398 | if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) { |
michael@0 | 399 | nscoord tx, ty; |
michael@0 | 400 | mFloatManager->GetTranslation(tx, ty); |
michael@0 | 401 | nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); |
michael@0 | 402 | printf("RecoverFloats: txy=%d,%d (%d,%d) ", |
michael@0 | 403 | tx, ty, mFloatManagerX, mFloatManagerY); |
michael@0 | 404 | nsFrame::ListTag(stdout, floatFrame); |
michael@0 | 405 | nsRect region = nsFloatManager::GetRegionFor(floatFrame); |
michael@0 | 406 | printf(" aDeltaY=%d region={%d,%d,%d,%d}\n", |
michael@0 | 407 | aDeltaY, region.x, region.y, region.width, region.height); |
michael@0 | 408 | } |
michael@0 | 409 | #endif |
michael@0 | 410 | mFloatManager->AddFloat(floatFrame, |
michael@0 | 411 | nsFloatManager::GetRegionFor(floatFrame)); |
michael@0 | 412 | fc = fc->Next(); |
michael@0 | 413 | } |
michael@0 | 414 | } else if (aLine->IsBlock()) { |
michael@0 | 415 | nsBlockFrame::RecoverFloatsFor(aLine->mFirstChild, *mFloatManager); |
michael@0 | 416 | } |
michael@0 | 417 | } |
michael@0 | 418 | |
michael@0 | 419 | /** |
michael@0 | 420 | * Everything done in this function is done O(N) times for each pass of |
michael@0 | 421 | * reflow so it is O(N*M) where M is the number of incremental reflow |
michael@0 | 422 | * passes. That's bad. Don't do stuff here. |
michael@0 | 423 | * |
michael@0 | 424 | * When this function is called, |aLine| has just been slid by |aDeltaY| |
michael@0 | 425 | * and the purpose of RecoverStateFrom is to ensure that the |
michael@0 | 426 | * nsBlockReflowState is in the same state that it would have been in |
michael@0 | 427 | * had the line just been reflowed. |
michael@0 | 428 | * |
michael@0 | 429 | * Most of the state recovery that we have to do involves floats. |
michael@0 | 430 | */ |
michael@0 | 431 | void |
michael@0 | 432 | nsBlockReflowState::RecoverStateFrom(nsLineList::iterator aLine, |
michael@0 | 433 | nscoord aDeltaY) |
michael@0 | 434 | { |
michael@0 | 435 | // Make the line being recovered the current line |
michael@0 | 436 | mCurrentLine = aLine; |
michael@0 | 437 | |
michael@0 | 438 | // Place floats for this line into the float manager |
michael@0 | 439 | if (aLine->HasFloats() || aLine->IsBlock()) { |
michael@0 | 440 | RecoverFloats(aLine, aDeltaY); |
michael@0 | 441 | |
michael@0 | 442 | #ifdef DEBUG |
michael@0 | 443 | if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) { |
michael@0 | 444 | mFloatManager->List(stdout); |
michael@0 | 445 | } |
michael@0 | 446 | #endif |
michael@0 | 447 | } |
michael@0 | 448 | } |
michael@0 | 449 | |
michael@0 | 450 | // This is called by the line layout's AddFloat method when a |
michael@0 | 451 | // place-holder frame is reflowed in a line. If the float is a |
michael@0 | 452 | // left-most child (it's x coordinate is at the line's left margin) |
michael@0 | 453 | // then the float is place immediately, otherwise the float |
michael@0 | 454 | // placement is deferred until the line has been reflowed. |
michael@0 | 455 | |
michael@0 | 456 | // XXXldb This behavior doesn't quite fit with CSS1 and CSS2 -- |
michael@0 | 457 | // technically we're supposed let the current line flow around the |
michael@0 | 458 | // float as well unless it won't fit next to what we already have. |
michael@0 | 459 | // But nobody else implements it that way... |
michael@0 | 460 | bool |
michael@0 | 461 | nsBlockReflowState::AddFloat(nsLineLayout* aLineLayout, |
michael@0 | 462 | nsIFrame* aFloat, |
michael@0 | 463 | nscoord aAvailableWidth) |
michael@0 | 464 | { |
michael@0 | 465 | NS_PRECONDITION(aLineLayout, "must have line layout"); |
michael@0 | 466 | NS_PRECONDITION(mBlock->end_lines() != mCurrentLine, "null ptr"); |
michael@0 | 467 | NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW, |
michael@0 | 468 | "aFloat must be an out-of-flow frame"); |
michael@0 | 469 | |
michael@0 | 470 | NS_ABORT_IF_FALSE(aFloat->GetParent(), "float must have parent"); |
michael@0 | 471 | NS_ABORT_IF_FALSE(aFloat->GetParent()->IsFrameOfType(nsIFrame::eBlockFrame), |
michael@0 | 472 | "float's parent must be block"); |
michael@0 | 473 | NS_ABORT_IF_FALSE(aFloat->GetParent() == mBlock || |
michael@0 | 474 | (aFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT), |
michael@0 | 475 | "float should be in this block unless it was marked as " |
michael@0 | 476 | "pushed float"); |
michael@0 | 477 | if (aFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT) { |
michael@0 | 478 | // If, in a previous reflow, the float was pushed entirely to |
michael@0 | 479 | // another column/page, we need to steal it back. (We might just |
michael@0 | 480 | // push it again, though.) Likewise, if that previous reflow |
michael@0 | 481 | // reflowed this block but not its next continuation, we might need |
michael@0 | 482 | // to steal it from our own float-continuations list. |
michael@0 | 483 | // |
michael@0 | 484 | // For more about pushed floats, see the comment above |
michael@0 | 485 | // nsBlockFrame::DrainPushedFloats. |
michael@0 | 486 | nsBlockFrame *floatParent = |
michael@0 | 487 | static_cast<nsBlockFrame*>(aFloat->GetParent()); |
michael@0 | 488 | floatParent->StealFrame(aFloat); |
michael@0 | 489 | |
michael@0 | 490 | aFloat->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT); |
michael@0 | 491 | |
michael@0 | 492 | // Appending is fine, since if a float was pushed to the next |
michael@0 | 493 | // page/column, all later floats were also pushed. |
michael@0 | 494 | mBlock->mFloats.AppendFrame(mBlock, aFloat); |
michael@0 | 495 | } |
michael@0 | 496 | |
michael@0 | 497 | // Because we are in the middle of reflowing a placeholder frame |
michael@0 | 498 | // within a line (and possibly nested in an inline frame or two |
michael@0 | 499 | // that's a child of our block) we need to restore the space |
michael@0 | 500 | // manager's translation to the space that the block resides in |
michael@0 | 501 | // before placing the float. |
michael@0 | 502 | nscoord ox, oy; |
michael@0 | 503 | mFloatManager->GetTranslation(ox, oy); |
michael@0 | 504 | nscoord dx = ox - mFloatManagerX; |
michael@0 | 505 | nscoord dy = oy - mFloatManagerY; |
michael@0 | 506 | mFloatManager->Translate(-dx, -dy); |
michael@0 | 507 | |
michael@0 | 508 | bool placed; |
michael@0 | 509 | |
michael@0 | 510 | // Now place the float immediately if possible. Otherwise stash it |
michael@0 | 511 | // away in mPendingFloats and place it later. |
michael@0 | 512 | // If one or more floats has already been pushed to the next line, |
michael@0 | 513 | // don't let this one go on the current line, since that would violate |
michael@0 | 514 | // float ordering. |
michael@0 | 515 | nsRect floatAvailableSpace = GetFloatAvailableSpace().mRect; |
michael@0 | 516 | if (mBelowCurrentLineFloats.IsEmpty() && |
michael@0 | 517 | (aLineLayout->LineIsEmpty() || |
michael@0 | 518 | mBlock->ComputeFloatWidth(*this, floatAvailableSpace, aFloat) |
michael@0 | 519 | <= aAvailableWidth)) { |
michael@0 | 520 | // And then place it |
michael@0 | 521 | placed = FlowAndPlaceFloat(aFloat); |
michael@0 | 522 | if (placed) { |
michael@0 | 523 | // Pass on updated available space to the current inline reflow engine |
michael@0 | 524 | nsFlowAreaRect floatAvailSpace = GetFloatAvailableSpace(mY); |
michael@0 | 525 | nsRect availSpace(nsPoint(floatAvailSpace.mRect.x, mY), |
michael@0 | 526 | floatAvailSpace.mRect.Size()); |
michael@0 | 527 | aLineLayout->UpdateBand(availSpace, aFloat); |
michael@0 | 528 | // Record this float in the current-line list |
michael@0 | 529 | mCurrentLineFloats.Append(mFloatCacheFreeList.Alloc(aFloat)); |
michael@0 | 530 | } else { |
michael@0 | 531 | (*aLineLayout->GetLine())->SetHadFloatPushed(); |
michael@0 | 532 | } |
michael@0 | 533 | } |
michael@0 | 534 | else { |
michael@0 | 535 | // Always claim to be placed; we don't know whether we fit yet, so we |
michael@0 | 536 | // deal with this in PlaceBelowCurrentLineFloats |
michael@0 | 537 | placed = true; |
michael@0 | 538 | // This float will be placed after the line is done (it is a |
michael@0 | 539 | // below-current-line float). |
michael@0 | 540 | mBelowCurrentLineFloats.Append(mFloatCacheFreeList.Alloc(aFloat)); |
michael@0 | 541 | } |
michael@0 | 542 | |
michael@0 | 543 | // Restore coordinate system |
michael@0 | 544 | mFloatManager->Translate(dx, dy); |
michael@0 | 545 | |
michael@0 | 546 | return placed; |
michael@0 | 547 | } |
michael@0 | 548 | |
michael@0 | 549 | bool |
michael@0 | 550 | nsBlockReflowState::CanPlaceFloat(nscoord aFloatWidth, |
michael@0 | 551 | const nsFlowAreaRect& aFloatAvailableSpace) |
michael@0 | 552 | { |
michael@0 | 553 | // A float fits at a given vertical position if there are no floats at |
michael@0 | 554 | // its horizontal position (no matter what its width) or if its width |
michael@0 | 555 | // fits in the space remaining after prior floats have been placed. |
michael@0 | 556 | // FIXME: We should allow overflow by up to half a pixel here (bug 21193). |
michael@0 | 557 | return !aFloatAvailableSpace.mHasFloats || |
michael@0 | 558 | aFloatAvailableSpace.mRect.width >= aFloatWidth; |
michael@0 | 559 | } |
michael@0 | 560 | |
michael@0 | 561 | static nscoord |
michael@0 | 562 | FloatMarginWidth(const nsHTMLReflowState& aCBReflowState, |
michael@0 | 563 | nscoord aFloatAvailableWidth, |
michael@0 | 564 | nsIFrame *aFloat, |
michael@0 | 565 | const nsCSSOffsetState& aFloatOffsetState) |
michael@0 | 566 | { |
michael@0 | 567 | AutoMaybeDisableFontInflation an(aFloat); |
michael@0 | 568 | return aFloat->ComputeSize( |
michael@0 | 569 | aCBReflowState.rendContext, |
michael@0 | 570 | nsSize(aCBReflowState.ComputedWidth(), |
michael@0 | 571 | aCBReflowState.ComputedHeight()), |
michael@0 | 572 | aFloatAvailableWidth, |
michael@0 | 573 | nsSize(aFloatOffsetState.ComputedPhysicalMargin().LeftRight(), |
michael@0 | 574 | aFloatOffsetState.ComputedPhysicalMargin().TopBottom()), |
michael@0 | 575 | nsSize(aFloatOffsetState.ComputedPhysicalBorderPadding().LeftRight() - |
michael@0 | 576 | aFloatOffsetState.ComputedPhysicalPadding().LeftRight(), |
michael@0 | 577 | aFloatOffsetState.ComputedPhysicalBorderPadding().TopBottom() - |
michael@0 | 578 | aFloatOffsetState.ComputedPhysicalPadding().TopBottom()), |
michael@0 | 579 | nsSize(aFloatOffsetState.ComputedPhysicalPadding().LeftRight(), |
michael@0 | 580 | aFloatOffsetState.ComputedPhysicalPadding().TopBottom()), |
michael@0 | 581 | true).width + |
michael@0 | 582 | aFloatOffsetState.ComputedPhysicalMargin().LeftRight() + |
michael@0 | 583 | aFloatOffsetState.ComputedPhysicalBorderPadding().LeftRight(); |
michael@0 | 584 | } |
michael@0 | 585 | |
michael@0 | 586 | bool |
michael@0 | 587 | nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat) |
michael@0 | 588 | { |
michael@0 | 589 | // Save away the Y coordinate before placing the float. We will |
michael@0 | 590 | // restore mY at the end after placing the float. This is |
michael@0 | 591 | // necessary because any adjustments to mY during the float |
michael@0 | 592 | // placement are for the float only, not for any non-floating |
michael@0 | 593 | // content. |
michael@0 | 594 | AutoRestore<nscoord> restoreY(mY); |
michael@0 | 595 | // FIXME: Should give AutoRestore a getter for the value to avoid this. |
michael@0 | 596 | const nscoord saveY = mY; |
michael@0 | 597 | |
michael@0 | 598 | // Grab the float's display information |
michael@0 | 599 | const nsStyleDisplay* floatDisplay = aFloat->StyleDisplay(); |
michael@0 | 600 | |
michael@0 | 601 | // The float's old region, so we can propagate damage. |
michael@0 | 602 | nsRect oldRegion = nsFloatManager::GetRegionFor(aFloat); |
michael@0 | 603 | |
michael@0 | 604 | // Enforce CSS2 9.5.1 rule [2], i.e., make sure that a float isn't |
michael@0 | 605 | // ``above'' another float that preceded it in the flow. |
michael@0 | 606 | mY = std::max(mFloatManager->GetLowestFloatTop(), mY); |
michael@0 | 607 | |
michael@0 | 608 | // See if the float should clear any preceding floats... |
michael@0 | 609 | // XXX We need to mark this float somehow so that it gets reflowed |
michael@0 | 610 | // when floats are inserted before it. |
michael@0 | 611 | if (NS_STYLE_CLEAR_NONE != floatDisplay->mBreakType) { |
michael@0 | 612 | // XXXldb Does this handle vertical margins correctly? |
michael@0 | 613 | mY = ClearFloats(mY, floatDisplay->mBreakType); |
michael@0 | 614 | } |
michael@0 | 615 | // Get the band of available space |
michael@0 | 616 | nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(mY); |
michael@0 | 617 | nsRect adjustedAvailableSpace = mBlock->AdjustFloatAvailableSpace(*this, |
michael@0 | 618 | floatAvailableSpace.mRect, aFloat); |
michael@0 | 619 | |
michael@0 | 620 | NS_ASSERTION(aFloat->GetParent() == mBlock, |
michael@0 | 621 | "Float frame has wrong parent"); |
michael@0 | 622 | |
michael@0 | 623 | nsCSSOffsetState offsets(aFloat, mReflowState.rendContext, |
michael@0 | 624 | mReflowState.ComputedWidth()); |
michael@0 | 625 | |
michael@0 | 626 | nscoord floatMarginWidth = FloatMarginWidth(mReflowState, |
michael@0 | 627 | adjustedAvailableSpace.width, |
michael@0 | 628 | aFloat, offsets); |
michael@0 | 629 | |
michael@0 | 630 | nsMargin floatMargin; // computed margin |
michael@0 | 631 | nsMargin floatOffsets; |
michael@0 | 632 | nsReflowStatus reflowStatus; |
michael@0 | 633 | |
michael@0 | 634 | // If it's a floating first-letter, we need to reflow it before we |
michael@0 | 635 | // know how wide it is (since we don't compute which letters are part |
michael@0 | 636 | // of the first letter until reflow!). |
michael@0 | 637 | bool isLetter = aFloat->GetType() == nsGkAtoms::letterFrame; |
michael@0 | 638 | if (isLetter) { |
michael@0 | 639 | mBlock->ReflowFloat(*this, adjustedAvailableSpace, aFloat, floatMargin, |
michael@0 | 640 | floatOffsets, false, reflowStatus); |
michael@0 | 641 | floatMarginWidth = aFloat->GetSize().width + floatMargin.LeftRight(); |
michael@0 | 642 | NS_ASSERTION(NS_FRAME_IS_COMPLETE(reflowStatus), |
michael@0 | 643 | "letter frames shouldn't break, and if they do now, " |
michael@0 | 644 | "then they're breaking at the wrong point"); |
michael@0 | 645 | } |
michael@0 | 646 | |
michael@0 | 647 | // Find a place to place the float. The CSS2 spec doesn't want |
michael@0 | 648 | // floats overlapping each other or sticking out of the containing |
michael@0 | 649 | // block if possible (CSS2 spec section 9.5.1, see the rule list). |
michael@0 | 650 | NS_ASSERTION((NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) || |
michael@0 | 651 | (NS_STYLE_FLOAT_RIGHT == floatDisplay->mFloats), |
michael@0 | 652 | "invalid float type"); |
michael@0 | 653 | |
michael@0 | 654 | // Can the float fit here? |
michael@0 | 655 | bool keepFloatOnSameLine = false; |
michael@0 | 656 | |
michael@0 | 657 | // Are we required to place at least part of the float because we're |
michael@0 | 658 | // at the top of the page (to avoid an infinite loop of pushing and |
michael@0 | 659 | // breaking). |
michael@0 | 660 | bool mustPlaceFloat = |
michael@0 | 661 | mReflowState.mFlags.mIsTopOfPage && IsAdjacentWithTop(); |
michael@0 | 662 | |
michael@0 | 663 | for (;;) { |
michael@0 | 664 | if (mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE && |
michael@0 | 665 | floatAvailableSpace.mRect.height <= 0 && |
michael@0 | 666 | !mustPlaceFloat) { |
michael@0 | 667 | // No space, nowhere to put anything. |
michael@0 | 668 | PushFloatPastBreak(aFloat); |
michael@0 | 669 | return false; |
michael@0 | 670 | } |
michael@0 | 671 | |
michael@0 | 672 | if (CanPlaceFloat(floatMarginWidth, floatAvailableSpace)) { |
michael@0 | 673 | // We found an appropriate place. |
michael@0 | 674 | break; |
michael@0 | 675 | } |
michael@0 | 676 | |
michael@0 | 677 | // Nope. try to advance to the next band. |
michael@0 | 678 | if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay || |
michael@0 | 679 | eCompatibility_NavQuirks != mPresContext->CompatibilityMode() ) { |
michael@0 | 680 | |
michael@0 | 681 | mY += floatAvailableSpace.mRect.height; |
michael@0 | 682 | if (adjustedAvailableSpace.height != NS_UNCONSTRAINEDSIZE) { |
michael@0 | 683 | adjustedAvailableSpace.height -= floatAvailableSpace.mRect.height; |
michael@0 | 684 | } |
michael@0 | 685 | floatAvailableSpace = GetFloatAvailableSpace(mY); |
michael@0 | 686 | } else { |
michael@0 | 687 | // This quirk matches the one in nsBlockFrame::AdjustFloatAvailableSpace |
michael@0 | 688 | // IE handles float tables in a very special way |
michael@0 | 689 | |
michael@0 | 690 | // see if the previous float is also a table and has "align" |
michael@0 | 691 | nsFloatCache* fc = mCurrentLineFloats.Head(); |
michael@0 | 692 | nsIFrame* prevFrame = nullptr; |
michael@0 | 693 | while (fc) { |
michael@0 | 694 | if (fc->mFloat == aFloat) { |
michael@0 | 695 | break; |
michael@0 | 696 | } |
michael@0 | 697 | prevFrame = fc->mFloat; |
michael@0 | 698 | fc = fc->Next(); |
michael@0 | 699 | } |
michael@0 | 700 | |
michael@0 | 701 | if(prevFrame) { |
michael@0 | 702 | //get the frame type |
michael@0 | 703 | if (nsGkAtoms::tableOuterFrame == prevFrame->GetType()) { |
michael@0 | 704 | //see if it has "align=" |
michael@0 | 705 | // IE makes a difference between align and he float property |
michael@0 | 706 | nsIContent* content = prevFrame->GetContent(); |
michael@0 | 707 | if (content) { |
michael@0 | 708 | // we're interested only if previous frame is align=left |
michael@0 | 709 | // IE messes things up when "right" (overlapping frames) |
michael@0 | 710 | if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::align, |
michael@0 | 711 | NS_LITERAL_STRING("left"), eIgnoreCase)) { |
michael@0 | 712 | keepFloatOnSameLine = true; |
michael@0 | 713 | // don't advance to next line (IE quirkie behaviour) |
michael@0 | 714 | // it breaks rule CSS2/9.5.1/1, but what the hell |
michael@0 | 715 | // since we cannot evangelize the world |
michael@0 | 716 | break; |
michael@0 | 717 | } |
michael@0 | 718 | } |
michael@0 | 719 | } |
michael@0 | 720 | } |
michael@0 | 721 | |
michael@0 | 722 | // the table does not fit anymore in this line so advance to next band |
michael@0 | 723 | mY += floatAvailableSpace.mRect.height; |
michael@0 | 724 | // To match nsBlockFrame::AdjustFloatAvailableSpace, we have to |
michael@0 | 725 | // get a new width for the new band. |
michael@0 | 726 | floatAvailableSpace = GetFloatAvailableSpace(mY); |
michael@0 | 727 | adjustedAvailableSpace = mBlock->AdjustFloatAvailableSpace(*this, |
michael@0 | 728 | floatAvailableSpace.mRect, aFloat); |
michael@0 | 729 | floatMarginWidth = FloatMarginWidth(mReflowState, |
michael@0 | 730 | adjustedAvailableSpace.width, |
michael@0 | 731 | aFloat, offsets); |
michael@0 | 732 | } |
michael@0 | 733 | |
michael@0 | 734 | mustPlaceFloat = false; |
michael@0 | 735 | } |
michael@0 | 736 | |
michael@0 | 737 | // If the float is continued, it will get the same absolute x value as its prev-in-flow |
michael@0 | 738 | |
michael@0 | 739 | // We don't worry about the geometry of the prev in flow, let the continuation |
michael@0 | 740 | // place and size itself as required. |
michael@0 | 741 | |
michael@0 | 742 | // Assign an x and y coordinate to the float. |
michael@0 | 743 | nscoord floatX, floatY; |
michael@0 | 744 | if (NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) { |
michael@0 | 745 | floatX = floatAvailableSpace.mRect.x; |
michael@0 | 746 | } |
michael@0 | 747 | else { |
michael@0 | 748 | if (!keepFloatOnSameLine) { |
michael@0 | 749 | floatX = floatAvailableSpace.mRect.XMost() - floatMarginWidth; |
michael@0 | 750 | } |
michael@0 | 751 | else { |
michael@0 | 752 | // this is the IE quirk (see few lines above) |
michael@0 | 753 | // the table is kept in the same line: don't let it overlap the |
michael@0 | 754 | // previous float |
michael@0 | 755 | floatX = floatAvailableSpace.mRect.x; |
michael@0 | 756 | } |
michael@0 | 757 | } |
michael@0 | 758 | // CSS2 spec, 9.5.1 rule [4]: "A floating box's outer top may not |
michael@0 | 759 | // be higher than the top of its containing block." (Since the |
michael@0 | 760 | // containing block is the content edge of the block box, this |
michael@0 | 761 | // means the margin edge of the float can't be higher than the |
michael@0 | 762 | // content edge of the block that contains it.) |
michael@0 | 763 | floatY = std::max(mY, mContentArea.y); |
michael@0 | 764 | |
michael@0 | 765 | // Reflow the float after computing its vertical position so it knows |
michael@0 | 766 | // where to break. |
michael@0 | 767 | if (!isLetter) { |
michael@0 | 768 | bool pushedDown = mY != saveY; |
michael@0 | 769 | mBlock->ReflowFloat(*this, adjustedAvailableSpace, aFloat, floatMargin, |
michael@0 | 770 | floatOffsets, pushedDown, reflowStatus); |
michael@0 | 771 | } |
michael@0 | 772 | if (aFloat->GetPrevInFlow()) |
michael@0 | 773 | floatMargin.top = 0; |
michael@0 | 774 | if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) |
michael@0 | 775 | floatMargin.bottom = 0; |
michael@0 | 776 | |
michael@0 | 777 | // In the case that we're in columns and not splitting floats, we need |
michael@0 | 778 | // to check here that the float's height fit, and if it didn't, bail. |
michael@0 | 779 | // (This code is only for DISABLE_FLOAT_BREAKING_IN_COLUMNS .) |
michael@0 | 780 | // |
michael@0 | 781 | // Likewise, if none of the float fit, and it needs to be pushed in |
michael@0 | 782 | // its entirety to the next page (NS_FRAME_IS_TRUNCATED or |
michael@0 | 783 | // NS_INLINE_IS_BREAK_BEFORE), we need to do the same. |
michael@0 | 784 | if ((mContentArea.height != NS_UNCONSTRAINEDSIZE && |
michael@0 | 785 | adjustedAvailableSpace.height == NS_UNCONSTRAINEDSIZE && |
michael@0 | 786 | !mustPlaceFloat && |
michael@0 | 787 | aFloat->GetSize().height + floatMargin.TopBottom() > |
michael@0 | 788 | mContentArea.YMost() - floatY) || |
michael@0 | 789 | NS_FRAME_IS_TRUNCATED(reflowStatus) || |
michael@0 | 790 | NS_INLINE_IS_BREAK_BEFORE(reflowStatus)) { |
michael@0 | 791 | PushFloatPastBreak(aFloat); |
michael@0 | 792 | return false; |
michael@0 | 793 | } |
michael@0 | 794 | |
michael@0 | 795 | // We can't use aFloat->ShouldAvoidBreakInside(mReflowState) here since |
michael@0 | 796 | // its mIsTopOfPage may be true even though the float isn't at the |
michael@0 | 797 | // top when floatY > 0. |
michael@0 | 798 | if (mContentArea.height != NS_UNCONSTRAINEDSIZE && |
michael@0 | 799 | !mustPlaceFloat && (!mReflowState.mFlags.mIsTopOfPage || floatY > 0) && |
michael@0 | 800 | NS_STYLE_PAGE_BREAK_AVOID == aFloat->StyleDisplay()->mBreakInside && |
michael@0 | 801 | (!NS_FRAME_IS_FULLY_COMPLETE(reflowStatus) || |
michael@0 | 802 | aFloat->GetSize().height + floatMargin.TopBottom() > |
michael@0 | 803 | mContentArea.YMost() - floatY) && |
michael@0 | 804 | !aFloat->GetPrevInFlow()) { |
michael@0 | 805 | PushFloatPastBreak(aFloat); |
michael@0 | 806 | return false; |
michael@0 | 807 | } |
michael@0 | 808 | |
michael@0 | 809 | // Calculate the actual origin of the float frame's border rect |
michael@0 | 810 | // relative to the parent block; the margin must be added in |
michael@0 | 811 | // to get the border rect |
michael@0 | 812 | nsPoint origin(floatMargin.left + floatX, |
michael@0 | 813 | floatMargin.top + floatY); |
michael@0 | 814 | |
michael@0 | 815 | // If float is relatively positioned, factor that in as well |
michael@0 | 816 | nsHTMLReflowState::ApplyRelativePositioning(aFloat, floatOffsets, &origin); |
michael@0 | 817 | |
michael@0 | 818 | // Position the float and make sure and views are properly |
michael@0 | 819 | // positioned. We need to explicitly position its child views as |
michael@0 | 820 | // well, since we're moving the float after flowing it. |
michael@0 | 821 | bool moved = aFloat->GetPosition() != origin; |
michael@0 | 822 | if (moved) { |
michael@0 | 823 | aFloat->SetPosition(origin); |
michael@0 | 824 | nsContainerFrame::PositionFrameView(aFloat); |
michael@0 | 825 | nsContainerFrame::PositionChildViews(aFloat); |
michael@0 | 826 | } |
michael@0 | 827 | |
michael@0 | 828 | // Update the float combined area state |
michael@0 | 829 | // XXX Floats should really just get invalidated here if necessary |
michael@0 | 830 | mFloatOverflowAreas.UnionWith(aFloat->GetOverflowAreas() + origin); |
michael@0 | 831 | |
michael@0 | 832 | // Place the float in the float manager |
michael@0 | 833 | // calculate region |
michael@0 | 834 | nsRect region = nsFloatManager::CalculateRegionFor(aFloat, floatMargin); |
michael@0 | 835 | // if the float split, then take up all of the vertical height |
michael@0 | 836 | if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus) && |
michael@0 | 837 | (NS_UNCONSTRAINEDSIZE != mContentArea.height)) { |
michael@0 | 838 | region.height = std::max(region.height, mContentArea.height - floatY); |
michael@0 | 839 | } |
michael@0 | 840 | DebugOnly<nsresult> rv = |
michael@0 | 841 | mFloatManager->AddFloat(aFloat, region); |
michael@0 | 842 | NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "bad float placement"); |
michael@0 | 843 | // store region |
michael@0 | 844 | nsFloatManager::StoreRegionFor(aFloat, region); |
michael@0 | 845 | |
michael@0 | 846 | // If the float's dimensions have changed, note the damage in the |
michael@0 | 847 | // float manager. |
michael@0 | 848 | if (!region.IsEqualEdges(oldRegion)) { |
michael@0 | 849 | // XXXwaterson conservative: we could probably get away with noting |
michael@0 | 850 | // less damage; e.g., if only height has changed, then only note the |
michael@0 | 851 | // area into which the float has grown or from which the float has |
michael@0 | 852 | // shrunk. |
michael@0 | 853 | nscoord top = std::min(region.y, oldRegion.y); |
michael@0 | 854 | nscoord bottom = std::max(region.YMost(), oldRegion.YMost()); |
michael@0 | 855 | mFloatManager->IncludeInDamage(top, bottom); |
michael@0 | 856 | } |
michael@0 | 857 | |
michael@0 | 858 | if (!NS_FRAME_IS_FULLY_COMPLETE(reflowStatus)) { |
michael@0 | 859 | mBlock->SplitFloat(*this, aFloat, reflowStatus); |
michael@0 | 860 | } |
michael@0 | 861 | |
michael@0 | 862 | #ifdef NOISY_FLOATMANAGER |
michael@0 | 863 | nscoord tx, ty; |
michael@0 | 864 | mFloatManager->GetTranslation(tx, ty); |
michael@0 | 865 | nsFrame::ListTag(stdout, mBlock); |
michael@0 | 866 | printf(": FlowAndPlaceFloat: AddFloat: txy=%d,%d (%d,%d) {%d,%d,%d,%d}\n", |
michael@0 | 867 | tx, ty, mFloatManagerX, mFloatManagerY, |
michael@0 | 868 | region.x, region.y, region.width, region.height); |
michael@0 | 869 | #endif |
michael@0 | 870 | |
michael@0 | 871 | #ifdef DEBUG |
michael@0 | 872 | if (nsBlockFrame::gNoisyReflow) { |
michael@0 | 873 | nsRect r = aFloat->GetRect(); |
michael@0 | 874 | nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); |
michael@0 | 875 | printf("placed float: "); |
michael@0 | 876 | nsFrame::ListTag(stdout, aFloat); |
michael@0 | 877 | printf(" %d,%d,%d,%d\n", r.x, r.y, r.width, r.height); |
michael@0 | 878 | } |
michael@0 | 879 | #endif |
michael@0 | 880 | |
michael@0 | 881 | return true; |
michael@0 | 882 | } |
michael@0 | 883 | |
michael@0 | 884 | void |
michael@0 | 885 | nsBlockReflowState::PushFloatPastBreak(nsIFrame *aFloat) |
michael@0 | 886 | { |
michael@0 | 887 | // This ensures that we: |
michael@0 | 888 | // * don't try to place later but smaller floats (which CSS says |
michael@0 | 889 | // must have their tops below the top of this float) |
michael@0 | 890 | // * don't waste much time trying to reflow this float again until |
michael@0 | 891 | // after the break |
michael@0 | 892 | if (aFloat->StyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) { |
michael@0 | 893 | mFloatManager->SetPushedLeftFloatPastBreak(); |
michael@0 | 894 | } else { |
michael@0 | 895 | NS_ABORT_IF_FALSE(aFloat->StyleDisplay()->mFloats == |
michael@0 | 896 | NS_STYLE_FLOAT_RIGHT, |
michael@0 | 897 | "unexpected float value"); |
michael@0 | 898 | mFloatManager->SetPushedRightFloatPastBreak(); |
michael@0 | 899 | } |
michael@0 | 900 | |
michael@0 | 901 | // Put the float on the pushed floats list, even though it |
michael@0 | 902 | // isn't actually a continuation. |
michael@0 | 903 | DebugOnly<nsresult> rv = mBlock->StealFrame(aFloat); |
michael@0 | 904 | NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame should succeed"); |
michael@0 | 905 | AppendPushedFloat(aFloat); |
michael@0 | 906 | |
michael@0 | 907 | NS_FRAME_SET_OVERFLOW_INCOMPLETE(mReflowStatus); |
michael@0 | 908 | } |
michael@0 | 909 | |
michael@0 | 910 | /** |
michael@0 | 911 | * Place below-current-line floats. |
michael@0 | 912 | */ |
michael@0 | 913 | void |
michael@0 | 914 | nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aList, |
michael@0 | 915 | nsLineBox* aLine) |
michael@0 | 916 | { |
michael@0 | 917 | nsFloatCache* fc = aList.Head(); |
michael@0 | 918 | while (fc) { |
michael@0 | 919 | #ifdef DEBUG |
michael@0 | 920 | if (nsBlockFrame::gNoisyReflow) { |
michael@0 | 921 | nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); |
michael@0 | 922 | printf("placing bcl float: "); |
michael@0 | 923 | nsFrame::ListTag(stdout, fc->mFloat); |
michael@0 | 924 | printf("\n"); |
michael@0 | 925 | } |
michael@0 | 926 | #endif |
michael@0 | 927 | // Place the float |
michael@0 | 928 | bool placed = FlowAndPlaceFloat(fc->mFloat); |
michael@0 | 929 | nsFloatCache *next = fc->Next(); |
michael@0 | 930 | if (!placed) { |
michael@0 | 931 | aList.Remove(fc); |
michael@0 | 932 | delete fc; |
michael@0 | 933 | aLine->SetHadFloatPushed(); |
michael@0 | 934 | } |
michael@0 | 935 | fc = next; |
michael@0 | 936 | } |
michael@0 | 937 | } |
michael@0 | 938 | |
michael@0 | 939 | nscoord |
michael@0 | 940 | nsBlockReflowState::ClearFloats(nscoord aY, uint8_t aBreakType, |
michael@0 | 941 | nsIFrame *aReplacedBlock, |
michael@0 | 942 | uint32_t aFlags) |
michael@0 | 943 | { |
michael@0 | 944 | #ifdef DEBUG |
michael@0 | 945 | if (nsBlockFrame::gNoisyReflow) { |
michael@0 | 946 | nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); |
michael@0 | 947 | printf("clear floats: in: aY=%d\n", aY); |
michael@0 | 948 | } |
michael@0 | 949 | #endif |
michael@0 | 950 | |
michael@0 | 951 | #ifdef NOISY_FLOAT_CLEARING |
michael@0 | 952 | printf("nsBlockReflowState::ClearFloats: aY=%d breakType=%d\n", |
michael@0 | 953 | aY, aBreakType); |
michael@0 | 954 | mFloatManager->List(stdout); |
michael@0 | 955 | #endif |
michael@0 | 956 | |
michael@0 | 957 | if (!mFloatManager->HasAnyFloats()) { |
michael@0 | 958 | return aY; |
michael@0 | 959 | } |
michael@0 | 960 | |
michael@0 | 961 | nscoord newY = aY; |
michael@0 | 962 | |
michael@0 | 963 | if (aBreakType != NS_STYLE_CLEAR_NONE) { |
michael@0 | 964 | newY = mFloatManager->ClearFloats(newY, aBreakType, aFlags); |
michael@0 | 965 | } |
michael@0 | 966 | |
michael@0 | 967 | if (aReplacedBlock) { |
michael@0 | 968 | for (;;) { |
michael@0 | 969 | nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(newY); |
michael@0 | 970 | if (!floatAvailableSpace.mHasFloats) { |
michael@0 | 971 | // If there aren't any floats here, then we always fit. |
michael@0 | 972 | // We check this before calling WidthToClearPastFloats, which is |
michael@0 | 973 | // somewhat expensive. |
michael@0 | 974 | break; |
michael@0 | 975 | } |
michael@0 | 976 | nsBlockFrame::ReplacedElementWidthToClear replacedWidth = |
michael@0 | 977 | nsBlockFrame::WidthToClearPastFloats(*this, floatAvailableSpace.mRect, |
michael@0 | 978 | aReplacedBlock); |
michael@0 | 979 | if (std::max(floatAvailableSpace.mRect.x - mContentArea.x, |
michael@0 | 980 | replacedWidth.marginLeft) + |
michael@0 | 981 | replacedWidth.borderBoxWidth + |
michael@0 | 982 | std::max(mContentArea.XMost() - floatAvailableSpace.mRect.XMost(), |
michael@0 | 983 | replacedWidth.marginRight) <= |
michael@0 | 984 | mContentArea.width) { |
michael@0 | 985 | break; |
michael@0 | 986 | } |
michael@0 | 987 | // See the analogous code for inlines in nsBlockFrame::DoReflowInlineFrames |
michael@0 | 988 | if (floatAvailableSpace.mRect.height > 0) { |
michael@0 | 989 | // See if there's room in the next band. |
michael@0 | 990 | newY += floatAvailableSpace.mRect.height; |
michael@0 | 991 | } else { |
michael@0 | 992 | if (mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE) { |
michael@0 | 993 | // Stop trying to clear here; we'll just get pushed to the |
michael@0 | 994 | // next column or page and try again there. |
michael@0 | 995 | break; |
michael@0 | 996 | } |
michael@0 | 997 | NS_NOTREACHED("avail space rect with zero height!"); |
michael@0 | 998 | newY += 1; |
michael@0 | 999 | } |
michael@0 | 1000 | } |
michael@0 | 1001 | } |
michael@0 | 1002 | |
michael@0 | 1003 | #ifdef DEBUG |
michael@0 | 1004 | if (nsBlockFrame::gNoisyReflow) { |
michael@0 | 1005 | nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); |
michael@0 | 1006 | printf("clear floats: out: y=%d\n", newY); |
michael@0 | 1007 | } |
michael@0 | 1008 | #endif |
michael@0 | 1009 | |
michael@0 | 1010 | return newY; |
michael@0 | 1011 | } |
michael@0 | 1012 |