layout/generic/nsFloatManager.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial