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 | /* class that manages rules for positioning floats */ |
michael@0 | 7 | |
michael@0 | 8 | #include "nsFloatManager.h" |
michael@0 | 9 | #include "nsIPresShell.h" |
michael@0 | 10 | #include "nsMemory.h" |
michael@0 | 11 | #include "nsHTMLReflowState.h" |
michael@0 | 12 | #include "nsBlockDebugFlags.h" |
michael@0 | 13 | #include "nsError.h" |
michael@0 | 14 | #include <algorithm> |
michael@0 | 15 | |
michael@0 | 16 | using namespace mozilla; |
michael@0 | 17 | |
michael@0 | 18 | int32_t nsFloatManager::sCachedFloatManagerCount = 0; |
michael@0 | 19 | void* nsFloatManager::sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE]; |
michael@0 | 20 | |
michael@0 | 21 | ///////////////////////////////////////////////////////////////////////////// |
michael@0 | 22 | |
michael@0 | 23 | // PresShell Arena allocate callback (for nsIntervalSet use below) |
michael@0 | 24 | static void* |
michael@0 | 25 | PSArenaAllocCB(size_t aSize, void* aClosure) |
michael@0 | 26 | { |
michael@0 | 27 | return static_cast<nsIPresShell*>(aClosure)->AllocateMisc(aSize); |
michael@0 | 28 | } |
michael@0 | 29 | |
michael@0 | 30 | // PresShell Arena free callback (for nsIntervalSet use below) |
michael@0 | 31 | static void |
michael@0 | 32 | PSArenaFreeCB(size_t aSize, void* aPtr, void* aClosure) |
michael@0 | 33 | { |
michael@0 | 34 | static_cast<nsIPresShell*>(aClosure)->FreeMisc(aSize, aPtr); |
michael@0 | 35 | } |
michael@0 | 36 | |
michael@0 | 37 | ///////////////////////////////////////////////////////////////////////////// |
michael@0 | 38 | // nsFloatManager |
michael@0 | 39 | |
michael@0 | 40 | nsFloatManager::nsFloatManager(nsIPresShell* aPresShell) |
michael@0 | 41 | : mX(0), mY(0), |
michael@0 | 42 | mFloatDamage(PSArenaAllocCB, PSArenaFreeCB, aPresShell), |
michael@0 | 43 | mPushedLeftFloatPastBreak(false), |
michael@0 | 44 | mPushedRightFloatPastBreak(false), |
michael@0 | 45 | mSplitLeftFloatAcrossBreak(false), |
michael@0 | 46 | mSplitRightFloatAcrossBreak(false) |
michael@0 | 47 | { |
michael@0 | 48 | MOZ_COUNT_CTOR(nsFloatManager); |
michael@0 | 49 | } |
michael@0 | 50 | |
michael@0 | 51 | nsFloatManager::~nsFloatManager() |
michael@0 | 52 | { |
michael@0 | 53 | MOZ_COUNT_DTOR(nsFloatManager); |
michael@0 | 54 | } |
michael@0 | 55 | |
michael@0 | 56 | // static |
michael@0 | 57 | void* nsFloatManager::operator new(size_t aSize) CPP_THROW_NEW |
michael@0 | 58 | { |
michael@0 | 59 | if (sCachedFloatManagerCount > 0) { |
michael@0 | 60 | // We have cached unused instances of this class, return a cached |
michael@0 | 61 | // instance in stead of always creating a new one. |
michael@0 | 62 | return sCachedFloatManagers[--sCachedFloatManagerCount]; |
michael@0 | 63 | } |
michael@0 | 64 | |
michael@0 | 65 | // The cache is empty, this means we haveto create a new instance using |
michael@0 | 66 | // the global |operator new|. |
michael@0 | 67 | return nsMemory::Alloc(aSize); |
michael@0 | 68 | } |
michael@0 | 69 | |
michael@0 | 70 | void |
michael@0 | 71 | nsFloatManager::operator delete(void* aPtr, size_t aSize) |
michael@0 | 72 | { |
michael@0 | 73 | if (!aPtr) |
michael@0 | 74 | return; |
michael@0 | 75 | // This float manager is no longer used, if there's still room in |
michael@0 | 76 | // the cache we'll cache this float manager, unless the layout |
michael@0 | 77 | // module was already shut down. |
michael@0 | 78 | |
michael@0 | 79 | if (sCachedFloatManagerCount < NS_FLOAT_MANAGER_CACHE_SIZE && |
michael@0 | 80 | sCachedFloatManagerCount >= 0) { |
michael@0 | 81 | // There's still space in the cache for more instances, put this |
michael@0 | 82 | // instance in the cache in stead of deleting it. |
michael@0 | 83 | |
michael@0 | 84 | sCachedFloatManagers[sCachedFloatManagerCount++] = aPtr; |
michael@0 | 85 | return; |
michael@0 | 86 | } |
michael@0 | 87 | |
michael@0 | 88 | // The cache is full, or the layout module has been shut down, |
michael@0 | 89 | // delete this float manager. |
michael@0 | 90 | nsMemory::Free(aPtr); |
michael@0 | 91 | } |
michael@0 | 92 | |
michael@0 | 93 | |
michael@0 | 94 | /* static */ |
michael@0 | 95 | void nsFloatManager::Shutdown() |
michael@0 | 96 | { |
michael@0 | 97 | // The layout module is being shut down, clean up the cache and |
michael@0 | 98 | // disable further caching. |
michael@0 | 99 | |
michael@0 | 100 | int32_t i; |
michael@0 | 101 | |
michael@0 | 102 | for (i = 0; i < sCachedFloatManagerCount; i++) { |
michael@0 | 103 | void* floatManager = sCachedFloatManagers[i]; |
michael@0 | 104 | if (floatManager) |
michael@0 | 105 | nsMemory::Free(floatManager); |
michael@0 | 106 | } |
michael@0 | 107 | |
michael@0 | 108 | // Disable further caching. |
michael@0 | 109 | sCachedFloatManagerCount = -1; |
michael@0 | 110 | } |
michael@0 | 111 | |
michael@0 | 112 | nsFlowAreaRect |
michael@0 | 113 | nsFloatManager::GetFlowArea(nscoord aYOffset, BandInfoType aInfoType, |
michael@0 | 114 | nscoord aHeight, nsRect aContentArea, |
michael@0 | 115 | SavedState* aState) const |
michael@0 | 116 | { |
michael@0 | 117 | NS_ASSERTION(aHeight >= 0, "unexpected max height"); |
michael@0 | 118 | NS_ASSERTION(aContentArea.width >= 0, "unexpected content area width"); |
michael@0 | 119 | |
michael@0 | 120 | nscoord top = aYOffset + mY; |
michael@0 | 121 | if (top < nscoord_MIN) { |
michael@0 | 122 | NS_WARNING("bad value"); |
michael@0 | 123 | top = nscoord_MIN; |
michael@0 | 124 | } |
michael@0 | 125 | |
michael@0 | 126 | // Determine the last float that we should consider. |
michael@0 | 127 | uint32_t floatCount; |
michael@0 | 128 | if (aState) { |
michael@0 | 129 | // Use the provided state. |
michael@0 | 130 | floatCount = aState->mFloatInfoCount; |
michael@0 | 131 | NS_ABORT_IF_FALSE(floatCount <= mFloats.Length(), "bad state"); |
michael@0 | 132 | } else { |
michael@0 | 133 | // Use our current state. |
michael@0 | 134 | floatCount = mFloats.Length(); |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | // If there are no floats at all, or we're below the last one, return |
michael@0 | 138 | // quickly. |
michael@0 | 139 | if (floatCount == 0 || |
michael@0 | 140 | (mFloats[floatCount-1].mLeftYMost <= top && |
michael@0 | 141 | mFloats[floatCount-1].mRightYMost <= top)) { |
michael@0 | 142 | return nsFlowAreaRect(aContentArea.x, aYOffset, aContentArea.width, |
michael@0 | 143 | aHeight, false); |
michael@0 | 144 | } |
michael@0 | 145 | |
michael@0 | 146 | nscoord bottom; |
michael@0 | 147 | if (aHeight == nscoord_MAX) { |
michael@0 | 148 | // This warning (and the two below) are possible to hit on pages |
michael@0 | 149 | // with really large objects. |
michael@0 | 150 | NS_WARN_IF_FALSE(aInfoType == BAND_FROM_POINT, |
michael@0 | 151 | "bad height"); |
michael@0 | 152 | bottom = nscoord_MAX; |
michael@0 | 153 | } else { |
michael@0 | 154 | bottom = top + aHeight; |
michael@0 | 155 | if (bottom < top || bottom > nscoord_MAX) { |
michael@0 | 156 | NS_WARNING("bad value"); |
michael@0 | 157 | bottom = nscoord_MAX; |
michael@0 | 158 | } |
michael@0 | 159 | } |
michael@0 | 160 | nscoord left = mX + aContentArea.x; |
michael@0 | 161 | nscoord right = mX + aContentArea.XMost(); |
michael@0 | 162 | if (right < left) { |
michael@0 | 163 | NS_WARNING("bad value"); |
michael@0 | 164 | right = left; |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | // Walk backwards through the floats until we either hit the front of |
michael@0 | 168 | // the list or we're above |top|. |
michael@0 | 169 | bool haveFloats = false; |
michael@0 | 170 | for (uint32_t i = floatCount; i > 0; --i) { |
michael@0 | 171 | const FloatInfo &fi = mFloats[i-1]; |
michael@0 | 172 | if (fi.mLeftYMost <= top && fi.mRightYMost <= top) { |
michael@0 | 173 | // There aren't any more floats that could intersect this band. |
michael@0 | 174 | break; |
michael@0 | 175 | } |
michael@0 | 176 | if (fi.mRect.IsEmpty()) { |
michael@0 | 177 | // For compatibility, ignore floats with empty rects, even though it |
michael@0 | 178 | // disagrees with the spec. (We might want to fix this in the |
michael@0 | 179 | // future, though.) |
michael@0 | 180 | continue; |
michael@0 | 181 | } |
michael@0 | 182 | nscoord floatTop = fi.mRect.y, floatBottom = fi.mRect.YMost(); |
michael@0 | 183 | if (top < floatTop && aInfoType == BAND_FROM_POINT) { |
michael@0 | 184 | // This float is below our band. Shrink our band's height if needed. |
michael@0 | 185 | if (floatTop < bottom) { |
michael@0 | 186 | bottom = floatTop; |
michael@0 | 187 | } |
michael@0 | 188 | } |
michael@0 | 189 | // If top == bottom (which happens only with WIDTH_WITHIN_HEIGHT), |
michael@0 | 190 | // we include floats that begin at our 0-height vertical area. We |
michael@0 | 191 | // need to to this to satisfy the invariant that a |
michael@0 | 192 | // WIDTH_WITHIN_HEIGHT call is at least as narrow on both sides as a |
michael@0 | 193 | // BAND_WITHIN_POINT call beginning at its top. |
michael@0 | 194 | else if (top < floatBottom && |
michael@0 | 195 | (floatTop < bottom || (floatTop == bottom && top == bottom))) { |
michael@0 | 196 | // This float is in our band. |
michael@0 | 197 | |
michael@0 | 198 | // Shrink our band's height if needed. |
michael@0 | 199 | if (floatBottom < bottom && aInfoType == BAND_FROM_POINT) { |
michael@0 | 200 | bottom = floatBottom; |
michael@0 | 201 | } |
michael@0 | 202 | |
michael@0 | 203 | // Shrink our band's width if needed. |
michael@0 | 204 | if (fi.mFrame->StyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) { |
michael@0 | 205 | // A left float. |
michael@0 | 206 | nscoord rightEdge = fi.mRect.XMost(); |
michael@0 | 207 | if (rightEdge > left) { |
michael@0 | 208 | left = rightEdge; |
michael@0 | 209 | // Only set haveFloats to true if the float is inside our |
michael@0 | 210 | // containing block. This matches the spec for what some |
michael@0 | 211 | // callers want and disagrees for other callers, so we should |
michael@0 | 212 | // probably provide better information at some point. |
michael@0 | 213 | haveFloats = true; |
michael@0 | 214 | } |
michael@0 | 215 | } else { |
michael@0 | 216 | // A right float. |
michael@0 | 217 | nscoord leftEdge = fi.mRect.x; |
michael@0 | 218 | if (leftEdge < right) { |
michael@0 | 219 | right = leftEdge; |
michael@0 | 220 | // See above. |
michael@0 | 221 | haveFloats = true; |
michael@0 | 222 | } |
michael@0 | 223 | } |
michael@0 | 224 | } |
michael@0 | 225 | } |
michael@0 | 226 | |
michael@0 | 227 | nscoord height = (bottom == nscoord_MAX) ? nscoord_MAX : (bottom - top); |
michael@0 | 228 | return nsFlowAreaRect(left - mX, top - mY, right - left, height, haveFloats); |
michael@0 | 229 | } |
michael@0 | 230 | |
michael@0 | 231 | nsresult |
michael@0 | 232 | nsFloatManager::AddFloat(nsIFrame* aFloatFrame, const nsRect& aMarginRect) |
michael@0 | 233 | { |
michael@0 | 234 | NS_ASSERTION(aMarginRect.width >= 0, "negative width!"); |
michael@0 | 235 | NS_ASSERTION(aMarginRect.height >= 0, "negative height!"); |
michael@0 | 236 | |
michael@0 | 237 | FloatInfo info(aFloatFrame, aMarginRect + nsPoint(mX, mY)); |
michael@0 | 238 | |
michael@0 | 239 | // Set mLeftYMost and mRightYMost. |
michael@0 | 240 | if (HasAnyFloats()) { |
michael@0 | 241 | FloatInfo &tail = mFloats[mFloats.Length() - 1]; |
michael@0 | 242 | info.mLeftYMost = tail.mLeftYMost; |
michael@0 | 243 | info.mRightYMost = tail.mRightYMost; |
michael@0 | 244 | } else { |
michael@0 | 245 | info.mLeftYMost = nscoord_MIN; |
michael@0 | 246 | info.mRightYMost = nscoord_MIN; |
michael@0 | 247 | } |
michael@0 | 248 | uint8_t floatStyle = aFloatFrame->StyleDisplay()->mFloats; |
michael@0 | 249 | NS_ASSERTION(floatStyle == NS_STYLE_FLOAT_LEFT || |
michael@0 | 250 | floatStyle == NS_STYLE_FLOAT_RIGHT, "unexpected float"); |
michael@0 | 251 | nscoord& sideYMost = (floatStyle == NS_STYLE_FLOAT_LEFT) ? info.mLeftYMost |
michael@0 | 252 | : info.mRightYMost; |
michael@0 | 253 | nscoord thisYMost = info.mRect.YMost(); |
michael@0 | 254 | if (thisYMost > sideYMost) |
michael@0 | 255 | sideYMost = thisYMost; |
michael@0 | 256 | |
michael@0 | 257 | if (!mFloats.AppendElement(info)) |
michael@0 | 258 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 259 | |
michael@0 | 260 | return NS_OK; |
michael@0 | 261 | } |
michael@0 | 262 | |
michael@0 | 263 | nsRect |
michael@0 | 264 | nsFloatManager::CalculateRegionFor(nsIFrame* aFloat, |
michael@0 | 265 | const nsMargin& aMargin) |
michael@0 | 266 | { |
michael@0 | 267 | // We consider relatively positioned frames at their original position. |
michael@0 | 268 | nsRect region(aFloat->GetNormalPosition(), aFloat->GetSize()); |
michael@0 | 269 | |
michael@0 | 270 | // Float region includes its margin |
michael@0 | 271 | region.Inflate(aMargin); |
michael@0 | 272 | |
michael@0 | 273 | // Don't store rectangles with negative margin-box width or height in |
michael@0 | 274 | // the float manager; it can't deal with them. |
michael@0 | 275 | if (region.width < 0) { |
michael@0 | 276 | // Preserve the right margin-edge for left floats and the left |
michael@0 | 277 | // margin-edge for right floats |
michael@0 | 278 | const nsStyleDisplay* display = aFloat->StyleDisplay(); |
michael@0 | 279 | if (NS_STYLE_FLOAT_LEFT == display->mFloats) { |
michael@0 | 280 | region.x = region.XMost(); |
michael@0 | 281 | } |
michael@0 | 282 | region.width = 0; |
michael@0 | 283 | } |
michael@0 | 284 | if (region.height < 0) { |
michael@0 | 285 | region.height = 0; |
michael@0 | 286 | } |
michael@0 | 287 | return region; |
michael@0 | 288 | } |
michael@0 | 289 | |
michael@0 | 290 | NS_DECLARE_FRAME_PROPERTY(FloatRegionProperty, nsIFrame::DestroyMargin) |
michael@0 | 291 | |
michael@0 | 292 | nsRect |
michael@0 | 293 | nsFloatManager::GetRegionFor(nsIFrame* aFloat) |
michael@0 | 294 | { |
michael@0 | 295 | nsRect region = aFloat->GetRect(); |
michael@0 | 296 | void* storedRegion = aFloat->Properties().Get(FloatRegionProperty()); |
michael@0 | 297 | if (storedRegion) { |
michael@0 | 298 | nsMargin margin = *static_cast<nsMargin*>(storedRegion); |
michael@0 | 299 | region.Inflate(margin); |
michael@0 | 300 | } |
michael@0 | 301 | return region; |
michael@0 | 302 | } |
michael@0 | 303 | |
michael@0 | 304 | void |
michael@0 | 305 | nsFloatManager::StoreRegionFor(nsIFrame* aFloat, |
michael@0 | 306 | nsRect& aRegion) |
michael@0 | 307 | { |
michael@0 | 308 | nsRect rect = aFloat->GetRect(); |
michael@0 | 309 | FrameProperties props = aFloat->Properties(); |
michael@0 | 310 | if (aRegion.IsEqualEdges(rect)) { |
michael@0 | 311 | props.Delete(FloatRegionProperty()); |
michael@0 | 312 | } |
michael@0 | 313 | else { |
michael@0 | 314 | nsMargin* storedMargin = static_cast<nsMargin*> |
michael@0 | 315 | (props.Get(FloatRegionProperty())); |
michael@0 | 316 | if (!storedMargin) { |
michael@0 | 317 | storedMargin = new nsMargin(); |
michael@0 | 318 | props.Set(FloatRegionProperty(), storedMargin); |
michael@0 | 319 | } |
michael@0 | 320 | *storedMargin = aRegion - rect; |
michael@0 | 321 | } |
michael@0 | 322 | } |
michael@0 | 323 | |
michael@0 | 324 | nsresult |
michael@0 | 325 | nsFloatManager::RemoveTrailingRegions(nsIFrame* aFrameList) |
michael@0 | 326 | { |
michael@0 | 327 | if (!aFrameList) { |
michael@0 | 328 | return NS_OK; |
michael@0 | 329 | } |
michael@0 | 330 | // This could be a good bit simpler if we could guarantee that the |
michael@0 | 331 | // floats given were at the end of our list, so we could just search |
michael@0 | 332 | // for the head of aFrameList. (But we can't; |
michael@0 | 333 | // layout/reftests/bugs/421710-1.html crashes.) |
michael@0 | 334 | nsTHashtable<nsPtrHashKey<nsIFrame> > frameSet(1); |
michael@0 | 335 | |
michael@0 | 336 | for (nsIFrame* f = aFrameList; f; f = f->GetNextSibling()) { |
michael@0 | 337 | frameSet.PutEntry(f); |
michael@0 | 338 | } |
michael@0 | 339 | |
michael@0 | 340 | uint32_t newLength = mFloats.Length(); |
michael@0 | 341 | while (newLength > 0) { |
michael@0 | 342 | if (!frameSet.Contains(mFloats[newLength - 1].mFrame)) { |
michael@0 | 343 | break; |
michael@0 | 344 | } |
michael@0 | 345 | --newLength; |
michael@0 | 346 | } |
michael@0 | 347 | mFloats.TruncateLength(newLength); |
michael@0 | 348 | |
michael@0 | 349 | #ifdef DEBUG |
michael@0 | 350 | for (uint32_t i = 0; i < mFloats.Length(); ++i) { |
michael@0 | 351 | NS_ASSERTION(!frameSet.Contains(mFloats[i].mFrame), |
michael@0 | 352 | "Frame region deletion was requested but we couldn't delete it"); |
michael@0 | 353 | } |
michael@0 | 354 | #endif |
michael@0 | 355 | |
michael@0 | 356 | return NS_OK; |
michael@0 | 357 | } |
michael@0 | 358 | |
michael@0 | 359 | void |
michael@0 | 360 | nsFloatManager::PushState(SavedState* aState) |
michael@0 | 361 | { |
michael@0 | 362 | NS_PRECONDITION(aState, "Need a place to save state"); |
michael@0 | 363 | |
michael@0 | 364 | // This is a cheap push implementation, which |
michael@0 | 365 | // only saves the (x,y) and last frame in the mFrameInfoMap |
michael@0 | 366 | // which is enough info to get us back to where we should be |
michael@0 | 367 | // when pop is called. |
michael@0 | 368 | // |
michael@0 | 369 | // This push/pop mechanism is used to undo any |
michael@0 | 370 | // floats that were added during the unconstrained reflow |
michael@0 | 371 | // in nsBlockReflowContext::DoReflowBlock(). (See bug 96736) |
michael@0 | 372 | // |
michael@0 | 373 | // It should also be noted that the state for mFloatDamage is |
michael@0 | 374 | // intentionally not saved or restored in PushState() and PopState(), |
michael@0 | 375 | // since that could lead to bugs where damage is missed/dropped when |
michael@0 | 376 | // we move from position A to B (during the intermediate incremental |
michael@0 | 377 | // reflow mentioned above) and then from B to C during the subsequent |
michael@0 | 378 | // reflow. In the typical case A and C will be the same, but not always. |
michael@0 | 379 | // Allowing mFloatDamage to accumulate the damage incurred during both |
michael@0 | 380 | // reflows ensures that nothing gets missed. |
michael@0 | 381 | aState->mX = mX; |
michael@0 | 382 | aState->mY = mY; |
michael@0 | 383 | aState->mPushedLeftFloatPastBreak = mPushedLeftFloatPastBreak; |
michael@0 | 384 | aState->mPushedRightFloatPastBreak = mPushedRightFloatPastBreak; |
michael@0 | 385 | aState->mSplitLeftFloatAcrossBreak = mSplitLeftFloatAcrossBreak; |
michael@0 | 386 | aState->mSplitRightFloatAcrossBreak = mSplitRightFloatAcrossBreak; |
michael@0 | 387 | aState->mFloatInfoCount = mFloats.Length(); |
michael@0 | 388 | } |
michael@0 | 389 | |
michael@0 | 390 | void |
michael@0 | 391 | nsFloatManager::PopState(SavedState* aState) |
michael@0 | 392 | { |
michael@0 | 393 | NS_PRECONDITION(aState, "No state to restore?"); |
michael@0 | 394 | |
michael@0 | 395 | mX = aState->mX; |
michael@0 | 396 | mY = aState->mY; |
michael@0 | 397 | mPushedLeftFloatPastBreak = aState->mPushedLeftFloatPastBreak; |
michael@0 | 398 | mPushedRightFloatPastBreak = aState->mPushedRightFloatPastBreak; |
michael@0 | 399 | mSplitLeftFloatAcrossBreak = aState->mSplitLeftFloatAcrossBreak; |
michael@0 | 400 | mSplitRightFloatAcrossBreak = aState->mSplitRightFloatAcrossBreak; |
michael@0 | 401 | |
michael@0 | 402 | NS_ASSERTION(aState->mFloatInfoCount <= mFloats.Length(), |
michael@0 | 403 | "somebody misused PushState/PopState"); |
michael@0 | 404 | mFloats.TruncateLength(aState->mFloatInfoCount); |
michael@0 | 405 | } |
michael@0 | 406 | |
michael@0 | 407 | nscoord |
michael@0 | 408 | nsFloatManager::GetLowestFloatTop() const |
michael@0 | 409 | { |
michael@0 | 410 | if (mPushedLeftFloatPastBreak || mPushedRightFloatPastBreak) { |
michael@0 | 411 | return nscoord_MAX; |
michael@0 | 412 | } |
michael@0 | 413 | if (!HasAnyFloats()) { |
michael@0 | 414 | return nscoord_MIN; |
michael@0 | 415 | } |
michael@0 | 416 | return mFloats[mFloats.Length() - 1].mRect.y - mY; |
michael@0 | 417 | } |
michael@0 | 418 | |
michael@0 | 419 | #ifdef DEBUG_FRAME_DUMP |
michael@0 | 420 | void |
michael@0 | 421 | DebugListFloatManager(const nsFloatManager *aFloatManager) |
michael@0 | 422 | { |
michael@0 | 423 | aFloatManager->List(stdout); |
michael@0 | 424 | } |
michael@0 | 425 | |
michael@0 | 426 | nsresult |
michael@0 | 427 | nsFloatManager::List(FILE* out) const |
michael@0 | 428 | { |
michael@0 | 429 | if (!HasAnyFloats()) |
michael@0 | 430 | return NS_OK; |
michael@0 | 431 | |
michael@0 | 432 | for (uint32_t i = 0; i < mFloats.Length(); ++i) { |
michael@0 | 433 | const FloatInfo &fi = mFloats[i]; |
michael@0 | 434 | fprintf_stderr(out, "Float %u: frame=%p rect={%d,%d,%d,%d} ymost={l:%d, r:%d}\n", |
michael@0 | 435 | i, static_cast<void*>(fi.mFrame), |
michael@0 | 436 | fi.mRect.x, fi.mRect.y, fi.mRect.width, fi.mRect.height, |
michael@0 | 437 | fi.mLeftYMost, fi.mRightYMost); |
michael@0 | 438 | } |
michael@0 | 439 | return NS_OK; |
michael@0 | 440 | } |
michael@0 | 441 | #endif |
michael@0 | 442 | |
michael@0 | 443 | nscoord |
michael@0 | 444 | nsFloatManager::ClearFloats(nscoord aY, uint8_t aBreakType, |
michael@0 | 445 | uint32_t aFlags) const |
michael@0 | 446 | { |
michael@0 | 447 | if (!(aFlags & DONT_CLEAR_PUSHED_FLOATS) && ClearContinues(aBreakType)) { |
michael@0 | 448 | return nscoord_MAX; |
michael@0 | 449 | } |
michael@0 | 450 | if (!HasAnyFloats()) { |
michael@0 | 451 | return aY; |
michael@0 | 452 | } |
michael@0 | 453 | |
michael@0 | 454 | nscoord bottom = aY + mY; |
michael@0 | 455 | |
michael@0 | 456 | const FloatInfo &tail = mFloats[mFloats.Length() - 1]; |
michael@0 | 457 | switch (aBreakType) { |
michael@0 | 458 | case NS_STYLE_CLEAR_BOTH: |
michael@0 | 459 | bottom = std::max(bottom, tail.mLeftYMost); |
michael@0 | 460 | bottom = std::max(bottom, tail.mRightYMost); |
michael@0 | 461 | break; |
michael@0 | 462 | case NS_STYLE_CLEAR_LEFT: |
michael@0 | 463 | bottom = std::max(bottom, tail.mLeftYMost); |
michael@0 | 464 | break; |
michael@0 | 465 | case NS_STYLE_CLEAR_RIGHT: |
michael@0 | 466 | bottom = std::max(bottom, tail.mRightYMost); |
michael@0 | 467 | break; |
michael@0 | 468 | default: |
michael@0 | 469 | // Do nothing |
michael@0 | 470 | break; |
michael@0 | 471 | } |
michael@0 | 472 | |
michael@0 | 473 | bottom -= mY; |
michael@0 | 474 | |
michael@0 | 475 | return bottom; |
michael@0 | 476 | } |
michael@0 | 477 | |
michael@0 | 478 | bool |
michael@0 | 479 | nsFloatManager::ClearContinues(uint8_t aBreakType) const |
michael@0 | 480 | { |
michael@0 | 481 | return ((mPushedLeftFloatPastBreak || mSplitLeftFloatAcrossBreak) && |
michael@0 | 482 | (aBreakType == NS_STYLE_CLEAR_BOTH || |
michael@0 | 483 | aBreakType == NS_STYLE_CLEAR_LEFT)) || |
michael@0 | 484 | ((mPushedRightFloatPastBreak || mSplitRightFloatAcrossBreak) && |
michael@0 | 485 | (aBreakType == NS_STYLE_CLEAR_BOTH || |
michael@0 | 486 | aBreakType == NS_STYLE_CLEAR_RIGHT)); |
michael@0 | 487 | } |
michael@0 | 488 | |
michael@0 | 489 | ///////////////////////////////////////////////////////////////////////////// |
michael@0 | 490 | // FloatInfo |
michael@0 | 491 | |
michael@0 | 492 | nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame, const nsRect& aRect) |
michael@0 | 493 | : mFrame(aFrame), mRect(aRect) |
michael@0 | 494 | { |
michael@0 | 495 | MOZ_COUNT_CTOR(nsFloatManager::FloatInfo); |
michael@0 | 496 | } |
michael@0 | 497 | |
michael@0 | 498 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 499 | nsFloatManager::FloatInfo::FloatInfo(const FloatInfo& aOther) |
michael@0 | 500 | : mFrame(aOther.mFrame), |
michael@0 | 501 | mRect(aOther.mRect), |
michael@0 | 502 | mLeftYMost(aOther.mLeftYMost), |
michael@0 | 503 | mRightYMost(aOther.mRightYMost) |
michael@0 | 504 | { |
michael@0 | 505 | MOZ_COUNT_CTOR(nsFloatManager::FloatInfo); |
michael@0 | 506 | } |
michael@0 | 507 | |
michael@0 | 508 | nsFloatManager::FloatInfo::~FloatInfo() |
michael@0 | 509 | { |
michael@0 | 510 | MOZ_COUNT_DTOR(nsFloatManager::FloatInfo); |
michael@0 | 511 | } |
michael@0 | 512 | #endif |
michael@0 | 513 | |
michael@0 | 514 | //---------------------------------------------------------------------- |
michael@0 | 515 | |
michael@0 | 516 | nsAutoFloatManager::~nsAutoFloatManager() |
michael@0 | 517 | { |
michael@0 | 518 | // Restore the old float manager in the reflow state if necessary. |
michael@0 | 519 | if (mNew) { |
michael@0 | 520 | #ifdef NOISY_FLOATMANAGER |
michael@0 | 521 | printf("restoring old float manager %p\n", mOld); |
michael@0 | 522 | #endif |
michael@0 | 523 | |
michael@0 | 524 | mReflowState.mFloatManager = mOld; |
michael@0 | 525 | |
michael@0 | 526 | #ifdef NOISY_FLOATMANAGER |
michael@0 | 527 | if (mOld) { |
michael@0 | 528 | static_cast<nsFrame *>(mReflowState.frame)->ListTag(stdout); |
michael@0 | 529 | printf(": space-manager %p after reflow\n", mOld); |
michael@0 | 530 | mOld->List(stdout); |
michael@0 | 531 | } |
michael@0 | 532 | #endif |
michael@0 | 533 | |
michael@0 | 534 | delete mNew; |
michael@0 | 535 | } |
michael@0 | 536 | } |
michael@0 | 537 | |
michael@0 | 538 | nsresult |
michael@0 | 539 | nsAutoFloatManager::CreateFloatManager(nsPresContext *aPresContext) |
michael@0 | 540 | { |
michael@0 | 541 | // Create a new float manager and install it in the reflow |
michael@0 | 542 | // state. `Remember' the old float manager so we can restore it |
michael@0 | 543 | // later. |
michael@0 | 544 | mNew = new nsFloatManager(aPresContext->PresShell()); |
michael@0 | 545 | if (! mNew) |
michael@0 | 546 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 547 | |
michael@0 | 548 | #ifdef NOISY_FLOATMANAGER |
michael@0 | 549 | printf("constructed new float manager %p (replacing %p)\n", |
michael@0 | 550 | mNew, mReflowState.mFloatManager); |
michael@0 | 551 | #endif |
michael@0 | 552 | |
michael@0 | 553 | // Set the float manager in the existing reflow state |
michael@0 | 554 | mOld = mReflowState.mFloatManager; |
michael@0 | 555 | mReflowState.mFloatManager = mNew; |
michael@0 | 556 | return NS_OK; |
michael@0 | 557 | } |