LCOV - code coverage report
Current view: top level - layout/generic - nsFloatManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 53 506 10.5 %
Date: 2017-07-14 16:53:18 Functions: 8 49 16.3 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       5             : 
       6             : /* class that manages rules for positioning floats */
       7             : 
       8             : #include "nsFloatManager.h"
       9             : 
      10             : #include <algorithm>
      11             : #include <initializer_list>
      12             : 
      13             : #include "mozilla/ReflowInput.h"
      14             : #include "mozilla/ShapeUtils.h"
      15             : #include "nsBlockFrame.h"
      16             : #include "nsError.h"
      17             : #include "nsIPresShell.h"
      18             : #include "nsMemory.h"
      19             : 
      20             : using namespace mozilla;
      21             : 
      22             : int32_t nsFloatManager::sCachedFloatManagerCount = 0;
      23             : void* nsFloatManager::sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE];
      24             : 
      25             : /////////////////////////////////////////////////////////////////////////////
      26             : // nsFloatManager
      27             : 
      28         152 : nsFloatManager::nsFloatManager(nsIPresShell* aPresShell,
      29         152 :                                mozilla::WritingMode aWM)
      30             :   :
      31             : #ifdef DEBUG
      32             :     mWritingMode(aWM),
      33             : #endif
      34             :     mLineLeft(0), mBlockStart(0),
      35             :     mFloatDamage(aPresShell),
      36             :     mPushedLeftFloatPastBreak(false),
      37             :     mPushedRightFloatPastBreak(false),
      38             :     mSplitLeftFloatAcrossBreak(false),
      39         152 :     mSplitRightFloatAcrossBreak(false)
      40             : {
      41         152 :   MOZ_COUNT_CTOR(nsFloatManager);
      42         152 : }
      43             : 
      44         304 : nsFloatManager::~nsFloatManager()
      45             : {
      46         152 :   MOZ_COUNT_DTOR(nsFloatManager);
      47         152 : }
      48             : 
      49             : // static
      50         152 : void* nsFloatManager::operator new(size_t aSize) CPP_THROW_NEW
      51             : {
      52         152 :   if (sCachedFloatManagerCount > 0) {
      53             :     // We have cached unused instances of this class, return a cached
      54             :     // instance in stead of always creating a new one.
      55         150 :     return sCachedFloatManagers[--sCachedFloatManagerCount];
      56             :   }
      57             : 
      58             :   // The cache is empty, this means we have to create a new instance using
      59             :   // the global |operator new|.
      60           2 :   return moz_xmalloc(aSize);
      61             : }
      62             : 
      63             : void
      64         152 : nsFloatManager::operator delete(void* aPtr, size_t aSize)
      65             : {
      66         152 :   if (!aPtr)
      67           0 :     return;
      68             :   // This float manager is no longer used, if there's still room in
      69             :   // the cache we'll cache this float manager, unless the layout
      70             :   // module was already shut down.
      71             : 
      72         304 :   if (sCachedFloatManagerCount < NS_FLOAT_MANAGER_CACHE_SIZE &&
      73         152 :       sCachedFloatManagerCount >= 0) {
      74             :     // There's still space in the cache for more instances, put this
      75             :     // instance in the cache in stead of deleting it.
      76             : 
      77         152 :     sCachedFloatManagers[sCachedFloatManagerCount++] = aPtr;
      78         152 :     return;
      79             :   }
      80             : 
      81             :   // The cache is full, or the layout module has been shut down,
      82             :   // delete this float manager.
      83           0 :   free(aPtr);
      84             : }
      85             : 
      86             : 
      87             : /* static */
      88           0 : void nsFloatManager::Shutdown()
      89             : {
      90             :   // The layout module is being shut down, clean up the cache and
      91             :   // disable further caching.
      92             : 
      93             :   int32_t i;
      94             : 
      95           0 :   for (i = 0; i < sCachedFloatManagerCount; i++) {
      96           0 :     void* floatManager = sCachedFloatManagers[i];
      97           0 :     if (floatManager)
      98           0 :       free(floatManager);
      99             :   }
     100             : 
     101             :   // Disable further caching.
     102           0 :   sCachedFloatManagerCount = -1;
     103           0 : }
     104             : 
     105             : #define CHECK_BLOCK_AND_LINE_DIR(aWM) \
     106             :   NS_ASSERTION((aWM).GetBlockDir() == mWritingMode.GetBlockDir() &&     \
     107             :                (aWM).IsLineInverted() == mWritingMode.IsLineInverted(), \
     108             :                "incompatible writing modes")
     109             : 
     110             : nsFlowAreaRect
     111         235 : nsFloatManager::GetFlowArea(WritingMode aWM, nscoord aBCoord, nscoord aBSize,
     112             :                             BandInfoType aBandInfoType, ShapeType aShapeType,
     113             :                             LogicalRect aContentArea, SavedState* aState,
     114             :                             const nsSize& aContainerSize) const
     115             : {
     116         235 :   CHECK_BLOCK_AND_LINE_DIR(aWM);
     117         235 :   NS_ASSERTION(aBSize >= 0, "unexpected max block size");
     118         235 :   NS_ASSERTION(aContentArea.ISize(aWM) >= 0,
     119             :                "unexpected content area inline size");
     120             : 
     121         235 :   nscoord blockStart = aBCoord + mBlockStart;
     122         235 :   if (blockStart < nscoord_MIN) {
     123           0 :     NS_WARNING("bad value");
     124           0 :     blockStart = nscoord_MIN;
     125             :   }
     126             : 
     127             :   // Determine the last float that we should consider.
     128             :   uint32_t floatCount;
     129         235 :   if (aState) {
     130             :     // Use the provided state.
     131           0 :     floatCount = aState->mFloatInfoCount;
     132           0 :     MOZ_ASSERT(floatCount <= mFloats.Length(), "bad state");
     133             :   } else {
     134             :     // Use our current state.
     135         235 :     floatCount = mFloats.Length();
     136             :   }
     137             : 
     138             :   // If there are no floats at all, or we're below the last one, return
     139             :   // quickly.
     140         470 :   if (floatCount == 0 ||
     141           0 :       (mFloats[floatCount-1].mLeftBEnd <= blockStart &&
     142           0 :        mFloats[floatCount-1].mRightBEnd <= blockStart)) {
     143         235 :     return nsFlowAreaRect(aWM, aContentArea.IStart(aWM), aBCoord,
     144         470 :                           aContentArea.ISize(aWM), aBSize, false);
     145             :   }
     146             : 
     147             :   nscoord blockEnd;
     148           0 :   if (aBSize == nscoord_MAX) {
     149             :     // This warning (and the two below) are possible to hit on pages
     150             :     // with really large objects.
     151           0 :     NS_WARNING_ASSERTION(aBandInfoType == BandInfoType::BandFromPoint, "bad height");
     152           0 :     blockEnd = nscoord_MAX;
     153             :   } else {
     154           0 :     blockEnd = blockStart + aBSize;
     155           0 :     if (blockEnd < blockStart || blockEnd > nscoord_MAX) {
     156           0 :       NS_WARNING("bad value");
     157           0 :       blockEnd = nscoord_MAX;
     158             :     }
     159             :   }
     160           0 :   nscoord lineLeft = mLineLeft + aContentArea.LineLeft(aWM, aContainerSize);
     161           0 :   nscoord lineRight = mLineLeft + aContentArea.LineRight(aWM, aContainerSize);
     162           0 :   if (lineRight < lineLeft) {
     163           0 :     NS_WARNING("bad value");
     164           0 :     lineRight = lineLeft;
     165             :   }
     166             : 
     167             :   // Walk backwards through the floats until we either hit the front of
     168             :   // the list or we're above |blockStart|.
     169           0 :   bool haveFloats = false;
     170           0 :   for (uint32_t i = floatCount; i > 0; --i) {
     171           0 :     const FloatInfo &fi = mFloats[i-1];
     172           0 :     if (fi.mLeftBEnd <= blockStart && fi.mRightBEnd <= blockStart) {
     173             :       // There aren't any more floats that could intersect this band.
     174           0 :       break;
     175             :     }
     176           0 :     if (fi.IsEmpty(aShapeType)) {
     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           0 :       continue;
     181             :     }
     182             : 
     183           0 :     nscoord floatBStart = fi.BStart(aShapeType);
     184           0 :     nscoord floatBEnd = fi.BEnd(aShapeType);
     185           0 :     if (blockStart < floatBStart && aBandInfoType == BandInfoType::BandFromPoint) {
     186             :       // This float is below our band.  Shrink our band's height if needed.
     187           0 :       if (floatBStart < blockEnd) {
     188           0 :         blockEnd = floatBStart;
     189             :       }
     190             :     }
     191             :     // If blockStart == blockEnd (which happens only with WidthWithinHeight),
     192             :     // we include floats that begin at our 0-height vertical area.  We
     193             :     // need to do this to satisfy the invariant that a
     194             :     // WidthWithinHeight call is at least as narrow on both sides as a
     195             :     // BandFromPoint call beginning at its blockStart.
     196           0 :     else if (blockStart < floatBEnd &&
     197           0 :              (floatBStart < blockEnd ||
     198           0 :               (floatBStart == blockEnd && blockStart == blockEnd))) {
     199             :       // This float is in our band.
     200             : 
     201             :       // Shrink our band's width if needed.
     202           0 :       StyleFloat floatStyle = fi.mFrame->StyleDisplay()->PhysicalFloats(aWM);
     203             : 
     204             :       // When aBandInfoType is BandFromPoint, we're only intended to
     205             :       // consider a point along the y axis rather than a band.
     206             :       const nscoord bandBlockEnd =
     207           0 :         aBandInfoType == BandInfoType::BandFromPoint ? blockStart : blockEnd;
     208           0 :       if (floatStyle == StyleFloat::Left) {
     209             :         // A left float
     210             :         nscoord lineRightEdge =
     211           0 :           fi.LineRight(aShapeType, blockStart, bandBlockEnd);
     212           0 :         if (lineRightEdge > lineLeft) {
     213           0 :           lineLeft = lineRightEdge;
     214             :           // Only set haveFloats to true if the float is inside our
     215             :           // containing block.  This matches the spec for what some
     216             :           // callers want and disagrees for other callers, so we should
     217             :           // probably provide better information at some point.
     218           0 :           haveFloats = true;
     219             :         }
     220             :       } else {
     221             :         // A right float
     222             :         nscoord lineLeftEdge =
     223           0 :           fi.LineLeft(aShapeType, blockStart, bandBlockEnd);
     224           0 :         if (lineLeftEdge < lineRight) {
     225           0 :           lineRight = lineLeftEdge;
     226             :           // See above.
     227           0 :           haveFloats = true;
     228             :         }
     229             :       }
     230             : 
     231             :       // Shrink our band's height if needed.
     232           0 :       if (floatBEnd < blockEnd && aBandInfoType == BandInfoType::BandFromPoint) {
     233           0 :         blockEnd = floatBEnd;
     234             :       }
     235             :     }
     236             :   }
     237             : 
     238           0 :   nscoord blockSize = (blockEnd == nscoord_MAX) ?
     239           0 :                        nscoord_MAX : (blockEnd - blockStart);
     240             :   // convert back from LineLeft/Right to IStart
     241           0 :   nscoord inlineStart = aWM.IsBidiLTR()
     242           0 :                         ? lineLeft - mLineLeft
     243           0 :                         : mLineLeft - lineRight +
     244           0 :                           LogicalSize(aWM, aContainerSize).ISize(aWM);
     245             : 
     246           0 :   return nsFlowAreaRect(aWM, inlineStart, blockStart - mBlockStart,
     247           0 :                         lineRight - lineLeft, blockSize, haveFloats);
     248             : }
     249             : 
     250             : void
     251           0 : nsFloatManager::AddFloat(nsIFrame* aFloatFrame, const LogicalRect& aMarginRect,
     252             :                          WritingMode aWM, const nsSize& aContainerSize)
     253             : {
     254           0 :   CHECK_BLOCK_AND_LINE_DIR(aWM);
     255           0 :   NS_ASSERTION(aMarginRect.ISize(aWM) >= 0, "negative inline size!");
     256           0 :   NS_ASSERTION(aMarginRect.BSize(aWM) >= 0, "negative block size!");
     257             : 
     258             :   FloatInfo info(aFloatFrame, mLineLeft, mBlockStart, aMarginRect, aWM,
     259           0 :                  aContainerSize);
     260             : 
     261             :   // Set mLeftBEnd and mRightBEnd.
     262           0 :   if (HasAnyFloats()) {
     263           0 :     FloatInfo &tail = mFloats[mFloats.Length() - 1];
     264           0 :     info.mLeftBEnd = tail.mLeftBEnd;
     265           0 :     info.mRightBEnd = tail.mRightBEnd;
     266             :   } else {
     267           0 :     info.mLeftBEnd = nscoord_MIN;
     268           0 :     info.mRightBEnd = nscoord_MIN;
     269             :   }
     270           0 :   StyleFloat floatStyle = aFloatFrame->StyleDisplay()->PhysicalFloats(aWM);
     271           0 :   MOZ_ASSERT(floatStyle == StyleFloat::Left || floatStyle == StyleFloat::Right,
     272             :              "Unexpected float style!");
     273             :   nscoord& sideBEnd =
     274           0 :     floatStyle == StyleFloat::Left ? info.mLeftBEnd : info.mRightBEnd;
     275           0 :   nscoord thisBEnd = info.BEnd();
     276           0 :   if (thisBEnd > sideBEnd)
     277           0 :     sideBEnd = thisBEnd;
     278             : 
     279           0 :   mFloats.AppendElement(Move(info));
     280           0 : }
     281             : 
     282             : // static
     283             : LogicalRect
     284           0 : nsFloatManager::CalculateRegionFor(WritingMode          aWM,
     285             :                                    nsIFrame*            aFloat,
     286             :                                    const LogicalMargin& aMargin,
     287             :                                    const nsSize&        aContainerSize)
     288             : {
     289             :   // We consider relatively positioned frames at their original position.
     290           0 :   LogicalRect region(aWM, nsRect(aFloat->GetNormalPosition(),
     291           0 :                                  aFloat->GetSize()),
     292           0 :                      aContainerSize);
     293             : 
     294             :   // Float region includes its margin
     295           0 :   region.Inflate(aWM, aMargin);
     296             : 
     297             :   // Don't store rectangles with negative margin-box width or height in
     298             :   // the float manager; it can't deal with them.
     299           0 :   if (region.ISize(aWM) < 0) {
     300             :     // Preserve the right margin-edge for left floats and the left
     301             :     // margin-edge for right floats
     302           0 :     const nsStyleDisplay* display = aFloat->StyleDisplay();
     303           0 :     StyleFloat floatStyle = display->PhysicalFloats(aWM);
     304           0 :     if ((StyleFloat::Left == floatStyle) == aWM.IsBidiLTR()) {
     305           0 :       region.IStart(aWM) = region.IEnd(aWM);
     306             :     }
     307           0 :     region.ISize(aWM) = 0;
     308             :   }
     309           0 :   if (region.BSize(aWM) < 0) {
     310           0 :     region.BSize(aWM) = 0;
     311             :   }
     312           0 :   return region;
     313             : }
     314             : 
     315           0 : NS_DECLARE_FRAME_PROPERTY_DELETABLE(FloatRegionProperty, nsMargin)
     316             : 
     317             : LogicalRect
     318           0 : nsFloatManager::GetRegionFor(WritingMode aWM, nsIFrame* aFloat,
     319             :                              const nsSize& aContainerSize)
     320             : {
     321           0 :   LogicalRect region = aFloat->GetLogicalRect(aWM, aContainerSize);
     322           0 :   void* storedRegion = aFloat->GetProperty(FloatRegionProperty());
     323           0 :   if (storedRegion) {
     324           0 :     nsMargin margin = *static_cast<nsMargin*>(storedRegion);
     325           0 :     region.Inflate(aWM, LogicalMargin(aWM, margin));
     326             :   }
     327           0 :   return region;
     328             : }
     329             : 
     330             : void
     331           0 : nsFloatManager::StoreRegionFor(WritingMode aWM, nsIFrame* aFloat,
     332             :                                const LogicalRect& aRegion,
     333             :                                const nsSize& aContainerSize)
     334             : {
     335           0 :   nsRect region = aRegion.GetPhysicalRect(aWM, aContainerSize);
     336           0 :   nsRect rect = aFloat->GetRect();
     337           0 :   if (region.IsEqualEdges(rect)) {
     338           0 :     aFloat->DeleteProperty(FloatRegionProperty());
     339             :   }
     340             :   else {
     341           0 :     nsMargin* storedMargin = aFloat->GetProperty(FloatRegionProperty());
     342           0 :     if (!storedMargin) {
     343           0 :       storedMargin = new nsMargin();
     344           0 :       aFloat->SetProperty(FloatRegionProperty(), storedMargin);
     345             :     }
     346           0 :     *storedMargin = region - rect;
     347             :   }
     348           0 : }
     349             : 
     350             : nsresult
     351           0 : nsFloatManager::RemoveTrailingRegions(nsIFrame* aFrameList)
     352             : {
     353           0 :   if (!aFrameList) {
     354           0 :     return NS_OK;
     355             :   }
     356             :   // This could be a good bit simpler if we could guarantee that the
     357             :   // floats given were at the end of our list, so we could just search
     358             :   // for the head of aFrameList.  (But we can't;
     359             :   // layout/reftests/bugs/421710-1.html crashes.)
     360           0 :   nsTHashtable<nsPtrHashKey<nsIFrame> > frameSet(1);
     361             : 
     362           0 :   for (nsIFrame* f = aFrameList; f; f = f->GetNextSibling()) {
     363           0 :     frameSet.PutEntry(f);
     364             :   }
     365             : 
     366           0 :   uint32_t newLength = mFloats.Length();
     367           0 :   while (newLength > 0) {
     368           0 :     if (!frameSet.Contains(mFloats[newLength - 1].mFrame)) {
     369           0 :       break;
     370             :     }
     371           0 :     --newLength;
     372             :   }
     373           0 :   mFloats.TruncateLength(newLength);
     374             : 
     375             : #ifdef DEBUG
     376           0 :   for (uint32_t i = 0; i < mFloats.Length(); ++i) {
     377           0 :     NS_ASSERTION(!frameSet.Contains(mFloats[i].mFrame),
     378             :                  "Frame region deletion was requested but we couldn't delete it");
     379             :   }
     380             : #endif
     381             : 
     382           0 :   return NS_OK;
     383             : }
     384             : 
     385             : void
     386         237 : nsFloatManager::PushState(SavedState* aState)
     387             : {
     388         237 :   NS_PRECONDITION(aState, "Need a place to save state");
     389             : 
     390             :   // This is a cheap push implementation, which
     391             :   // only saves the (x,y) and last frame in the mFrameInfoMap
     392             :   // which is enough info to get us back to where we should be
     393             :   // when pop is called.
     394             :   //
     395             :   // This push/pop mechanism is used to undo any
     396             :   // floats that were added during the unconstrained reflow
     397             :   // in nsBlockReflowContext::DoReflowBlock(). (See bug 96736)
     398             :   //
     399             :   // It should also be noted that the state for mFloatDamage is
     400             :   // intentionally not saved or restored in PushState() and PopState(),
     401             :   // since that could lead to bugs where damage is missed/dropped when
     402             :   // we move from position A to B (during the intermediate incremental
     403             :   // reflow mentioned above) and then from B to C during the subsequent
     404             :   // reflow. In the typical case A and C will be the same, but not always.
     405             :   // Allowing mFloatDamage to accumulate the damage incurred during both
     406             :   // reflows ensures that nothing gets missed.
     407         237 :   aState->mLineLeft = mLineLeft;
     408         237 :   aState->mBlockStart = mBlockStart;
     409         237 :   aState->mPushedLeftFloatPastBreak = mPushedLeftFloatPastBreak;
     410         237 :   aState->mPushedRightFloatPastBreak = mPushedRightFloatPastBreak;
     411         237 :   aState->mSplitLeftFloatAcrossBreak = mSplitLeftFloatAcrossBreak;
     412         237 :   aState->mSplitRightFloatAcrossBreak = mSplitRightFloatAcrossBreak;
     413         237 :   aState->mFloatInfoCount = mFloats.Length();
     414         237 : }
     415             : 
     416             : void
     417           0 : nsFloatManager::PopState(SavedState* aState)
     418             : {
     419           0 :   NS_PRECONDITION(aState, "No state to restore?");
     420             : 
     421           0 :   mLineLeft = aState->mLineLeft;
     422           0 :   mBlockStart = aState->mBlockStart;
     423           0 :   mPushedLeftFloatPastBreak = aState->mPushedLeftFloatPastBreak;
     424           0 :   mPushedRightFloatPastBreak = aState->mPushedRightFloatPastBreak;
     425           0 :   mSplitLeftFloatAcrossBreak = aState->mSplitLeftFloatAcrossBreak;
     426           0 :   mSplitRightFloatAcrossBreak = aState->mSplitRightFloatAcrossBreak;
     427             : 
     428           0 :   NS_ASSERTION(aState->mFloatInfoCount <= mFloats.Length(),
     429             :                "somebody misused PushState/PopState");
     430           0 :   mFloats.TruncateLength(aState->mFloatInfoCount);
     431           0 : }
     432             : 
     433             : nscoord
     434           0 : nsFloatManager::GetLowestFloatTop() const
     435             : {
     436           0 :   if (mPushedLeftFloatPastBreak || mPushedRightFloatPastBreak) {
     437           0 :     return nscoord_MAX;
     438             :   }
     439           0 :   if (!HasAnyFloats()) {
     440           0 :     return nscoord_MIN;
     441             :   }
     442           0 :   return mFloats[mFloats.Length() -1].BStart() - mBlockStart;
     443             : }
     444             : 
     445             : #ifdef DEBUG_FRAME_DUMP
     446             : void
     447           0 : DebugListFloatManager(const nsFloatManager *aFloatManager)
     448             : {
     449           0 :   aFloatManager->List(stdout);
     450           0 : }
     451             : 
     452             : nsresult
     453           0 : nsFloatManager::List(FILE* out) const
     454             : {
     455           0 :   if (!HasAnyFloats())
     456           0 :     return NS_OK;
     457             : 
     458           0 :   for (uint32_t i = 0; i < mFloats.Length(); ++i) {
     459           0 :     const FloatInfo &fi = mFloats[i];
     460           0 :     fprintf_stderr(out, "Float %u: frame=%p rect={%d,%d,%d,%d} BEnd={l:%d, r:%d}\n",
     461           0 :                    i, static_cast<void*>(fi.mFrame),
     462             :                    fi.LineLeft(), fi.BStart(), fi.ISize(), fi.BSize(),
     463           0 :                    fi.mLeftBEnd, fi.mRightBEnd);
     464             :   }
     465           0 :   return NS_OK;
     466             : }
     467             : #endif
     468             : 
     469             : nscoord
     470           0 : nsFloatManager::ClearFloats(nscoord aBCoord, StyleClear aBreakType,
     471             :                             uint32_t aFlags) const
     472             : {
     473           0 :   if (!(aFlags & DONT_CLEAR_PUSHED_FLOATS) && ClearContinues(aBreakType)) {
     474           0 :     return nscoord_MAX;
     475             :   }
     476           0 :   if (!HasAnyFloats()) {
     477           0 :     return aBCoord;
     478             :   }
     479             : 
     480           0 :   nscoord blockEnd = aBCoord + mBlockStart;
     481             : 
     482           0 :   const FloatInfo &tail = mFloats[mFloats.Length() - 1];
     483           0 :   switch (aBreakType) {
     484             :     case StyleClear::Both:
     485           0 :       blockEnd = std::max(blockEnd, tail.mLeftBEnd);
     486           0 :       blockEnd = std::max(blockEnd, tail.mRightBEnd);
     487           0 :       break;
     488             :     case StyleClear::Left:
     489           0 :       blockEnd = std::max(blockEnd, tail.mLeftBEnd);
     490           0 :       break;
     491             :     case StyleClear::Right:
     492           0 :       blockEnd = std::max(blockEnd, tail.mRightBEnd);
     493           0 :       break;
     494             :     default:
     495             :       // Do nothing
     496           0 :       break;
     497             :   }
     498             : 
     499           0 :   blockEnd -= mBlockStart;
     500             : 
     501           0 :   return blockEnd;
     502             : }
     503             : 
     504             : bool
     505           0 : nsFloatManager::ClearContinues(StyleClear aBreakType) const
     506             : {
     507           0 :   return ((mPushedLeftFloatPastBreak || mSplitLeftFloatAcrossBreak) &&
     508           0 :           (aBreakType == StyleClear::Both ||
     509           0 :            aBreakType == StyleClear::Left)) ||
     510           0 :          ((mPushedRightFloatPastBreak || mSplitRightFloatAcrossBreak) &&
     511           0 :           (aBreakType == StyleClear::Both ||
     512           0 :            aBreakType == StyleClear::Right));
     513             : }
     514             : 
     515             : /////////////////////////////////////////////////////////////////////////////
     516             : // RoundedBoxShapeInfo
     517             : 
     518             : nscoord
     519           0 : nsFloatManager::RoundedBoxShapeInfo::LineLeft(const nscoord aBStart,
     520             :                                               const nscoord aBEnd) const
     521             : {
     522           0 :   if (!mRadii) {
     523           0 :     return mRect.x;
     524             :   }
     525             : 
     526             :   nscoord lineLeftDiff =
     527           0 :     ComputeEllipseLineInterceptDiff(
     528           0 :       mRect.y, mRect.YMost(),
     529           0 :       mRadii[eCornerTopLeftX], mRadii[eCornerTopLeftY],
     530           0 :       mRadii[eCornerBottomLeftX], mRadii[eCornerBottomLeftY],
     531           0 :       aBStart, aBEnd);
     532           0 :   return mRect.x + lineLeftDiff;
     533             : }
     534             : 
     535             : nscoord
     536           0 : nsFloatManager::RoundedBoxShapeInfo::LineRight(const nscoord aBStart,
     537             :                                                const nscoord aBEnd) const
     538             : {
     539           0 :   if (!mRadii) {
     540           0 :     return mRect.XMost();
     541             :   }
     542             : 
     543             :   nscoord lineRightDiff =
     544           0 :     ComputeEllipseLineInterceptDiff(
     545           0 :       mRect.y, mRect.YMost(),
     546           0 :       mRadii[eCornerTopRightX], mRadii[eCornerTopRightY],
     547           0 :       mRadii[eCornerBottomRightX], mRadii[eCornerBottomRightY],
     548           0 :       aBStart, aBEnd);
     549           0 :   return mRect.XMost() - lineRightDiff;
     550             : }
     551             : 
     552             : /////////////////////////////////////////////////////////////////////////////
     553             : // EllipseShapeInfo
     554             : 
     555             : nscoord
     556           0 : nsFloatManager::EllipseShapeInfo::LineLeft(const nscoord aBStart,
     557             :                                            const nscoord aBEnd) const
     558             : {
     559             :   nscoord lineLeftDiff =
     560           0 :     ComputeEllipseLineInterceptDiff(BStart(), BEnd(),
     561           0 :                                     mRadii.width, mRadii.height,
     562           0 :                                     mRadii.width, mRadii.height,
     563           0 :                                     aBStart, aBEnd);
     564           0 :   return mCenter.x - mRadii.width + lineLeftDiff;
     565             : }
     566             : 
     567             : nscoord
     568           0 : nsFloatManager::EllipseShapeInfo::LineRight(const nscoord aBStart,
     569             :                                             const nscoord aBEnd) const
     570             : {
     571             :   nscoord lineRightDiff =
     572           0 :     ComputeEllipseLineInterceptDiff(BStart(), BEnd(),
     573           0 :                                     mRadii.width, mRadii.height,
     574           0 :                                     mRadii.width, mRadii.height,
     575           0 :                                     aBStart, aBEnd);
     576           0 :   return mCenter.x + mRadii.width - lineRightDiff;
     577             : }
     578             : 
     579             : /////////////////////////////////////////////////////////////////////////////
     580             : // PolygonShapeInfo
     581             : 
     582           0 : nsFloatManager::PolygonShapeInfo::PolygonShapeInfo(nsTArray<nsPoint>&& aVertices)
     583           0 :   : mVertices(aVertices)
     584             : {
     585             :   // Polygons with fewer than three vertices result in an empty area.
     586             :   // https://drafts.csswg.org/css-shapes/#funcdef-polygon
     587           0 :   if (mVertices.Length() < 3) {
     588           0 :     mEmpty = true;
     589           0 :     return;
     590             :   }
     591             : 
     592           0 :   auto Determinant = [] (const nsPoint& aP0, const nsPoint& aP1) {
     593             :     // Returns the determinant of the 2x2 matrix [aP0 aP1].
     594             :     // https://en.wikipedia.org/wiki/Determinant#2_.C3.97_2_matrices
     595           0 :     return aP0.x * aP1.y - aP0.y * aP1.x;
     596           0 :   };
     597             : 
     598             :   // See if we have any vertices that are non-collinear with the first two.
     599             :   // (If a polygon's vertices are all collinear, it encloses no area.)
     600           0 :   bool isEntirelyCollinear = true;
     601           0 :   const nsPoint& p0 = mVertices[0];
     602           0 :   const nsPoint& p1 = mVertices[1];
     603           0 :   for (size_t i = 2; i < mVertices.Length(); ++i) {
     604           0 :     const nsPoint& p2 = mVertices[i];
     605             : 
     606             :     // If the determinant of the matrix formed by two points is 0, that
     607             :     // means they're collinear with respect to the origin. Here, if it's
     608             :     // nonzero, then p1 and p2 are non-collinear with respect to p0, i.e.
     609             :     // the three points are non-collinear.
     610           0 :     if (Determinant(p2 - p0, p1 - p0) != 0) {
     611           0 :       isEntirelyCollinear = false;
     612           0 :       break;
     613             :     }
     614             :   }
     615             : 
     616           0 :   if (isEntirelyCollinear) {
     617           0 :     mEmpty = true;
     618           0 :     return;
     619             :   }
     620             : 
     621             :   // mBStart and mBEnd are the lower and the upper bounds of all the
     622             :   // vertex.y, respectively. The vertex.y is actually on the block-axis of
     623             :   // the float manager's writing mode.
     624           0 :   for (const nsPoint& vertex : mVertices) {
     625           0 :     mBStart = std::min(mBStart, vertex.y);
     626           0 :     mBEnd = std::max(mBEnd, vertex.y);
     627             :   }
     628             : }
     629             : 
     630             : nscoord
     631           0 : nsFloatManager::PolygonShapeInfo::LineLeft(const nscoord aBStart,
     632             :                                            const nscoord aBEnd) const
     633             : {
     634           0 :   MOZ_ASSERT(!mEmpty, "Shouldn't be called if the polygon encloses no area.");
     635             : 
     636             :   // We want the line-left-most inline-axis coordinate where the
     637             :   // (block-axis) aBStart/aBEnd band crosses a line segment of the polygon.
     638             :   // To get that, we start as line-right as possible (at nscoord_MAX). Then
     639             :   // we iterate each line segment to compute its intersection point with the
     640             :   // band (if any) and using std::min() successively to get the smallest
     641             :   // inline-coordinates among those intersection points.
     642             :   //
     643             :   // Note: std::min<nscoord> means the function std::min() with template
     644             :   // parameter nscoord, not the minimum value of nscoord.
     645           0 :   return ComputeLineIntercept(aBStart, aBEnd, std::min<nscoord>, nscoord_MAX);
     646             : }
     647             : 
     648             : nscoord
     649           0 : nsFloatManager::PolygonShapeInfo::LineRight(const nscoord aBStart,
     650             :                                             const nscoord aBEnd) const
     651             : {
     652           0 :   MOZ_ASSERT(!mEmpty, "Shouldn't be called if the polygon encloses no area.");
     653             : 
     654             :   // Similar to LineLeft(). Though here, we want the line-right-most
     655             :   // inline-axis coordinate, so we instead start at nscoord_MIN and use
     656             :   // std::max() to get the biggest inline-coordinate among those
     657             :   // intersection points.
     658           0 :   return ComputeLineIntercept(aBStart, aBEnd, std::max<nscoord>, nscoord_MIN);
     659             : }
     660             : 
     661             : nscoord
     662           0 : nsFloatManager::PolygonShapeInfo::ComputeLineIntercept(
     663             :   const nscoord aBStart,
     664             :   const nscoord aBEnd,
     665             :   nscoord (*aCompareOp) (std::initializer_list<nscoord>),
     666             :   const nscoord aLineInterceptInitialValue) const
     667             : {
     668           0 :   MOZ_ASSERT(aBStart <= aBEnd,
     669             :              "The band's block start is greater than its block end?");
     670             : 
     671           0 :   const size_t len = mVertices.Length();
     672           0 :   nscoord lineIntercept = aLineInterceptInitialValue;
     673             : 
     674             :   // Iterate each line segment {p0, p1}, {p1, p2}, ..., {pn, p0}.
     675           0 :   for (size_t i = 0; i < len; ++i) {
     676           0 :     const nsPoint* smallYVertex = &mVertices[i];
     677           0 :     const nsPoint* bigYVertex = &mVertices[(i + 1) % len];
     678             : 
     679             :     // Swap the two points to satisfy the requirement for calling
     680             :     // XInterceptAtY.
     681           0 :     if (smallYVertex->y > bigYVertex->y) {
     682           0 :       std::swap(smallYVertex, bigYVertex);
     683             :     }
     684             : 
     685           0 :     if (aBStart >= bigYVertex->y || aBEnd <= smallYVertex->y ||
     686           0 :         smallYVertex->y == bigYVertex->y) {
     687             :       // Skip computing the intercept if a) the band doesn't intersect the
     688             :       // line segment (even if it crosses one of two the vertices); or b)
     689             :       // the line segment is horizontal. It's OK because the two end points
     690             :       // forming this horizontal segment will still be considered if each of
     691             :       // them is forming another non-horizontal segment with other points.
     692           0 :       continue;
     693             :     }
     694             : 
     695             :     nscoord bStartLineIntercept =
     696           0 :       aBStart <= smallYVertex->y
     697           0 :         ? smallYVertex->x
     698           0 :         : XInterceptAtY(aBStart, *smallYVertex, *bigYVertex);
     699             :     nscoord bEndLineIntercept =
     700           0 :       aBEnd >= bigYVertex->y
     701           0 :         ? bigYVertex->x
     702           0 :         : XInterceptAtY(aBEnd, *smallYVertex, *bigYVertex);
     703             : 
     704             :     // If either new intercept is more extreme than lineIntercept (per
     705             :     // aCompareOp), then update lineIntercept to that value.
     706             :     lineIntercept =
     707           0 :       aCompareOp({lineIntercept, bStartLineIntercept, bEndLineIntercept});
     708             :   }
     709             : 
     710           0 :   return lineIntercept;
     711             : }
     712             : 
     713             : void
     714           0 : nsFloatManager::PolygonShapeInfo::Translate(nscoord aLineLeft,
     715             :                                             nscoord aBlockStart)
     716             : {
     717           0 :   for (nsPoint& vertex : mVertices) {
     718           0 :     vertex.MoveBy(aLineLeft, aBlockStart);
     719             :   }
     720           0 :   mBStart += aBlockStart;
     721           0 :   mBEnd += aBlockStart;
     722           0 : }
     723             : 
     724             : /* static */ nscoord
     725           0 : nsFloatManager::PolygonShapeInfo::XInterceptAtY(const nscoord aY,
     726             :                                                 const nsPoint& aP1,
     727             :                                                 const nsPoint& aP2)
     728             : {
     729             :   // Solve for x in the linear equation: x = x1 + (y-y1) * (x2-x1) / (y2-y1),
     730             :   // where aP1 = (x1, y1) and aP2 = (x2, y2).
     731             : 
     732           0 :   MOZ_ASSERT(aP1.y <= aY && aY <= aP2.y,
     733             :              "This function won't work if the horizontal line at aY and "
     734             :              "the line segment (aP1, aP2) do not intersect!");
     735             : 
     736           0 :   MOZ_ASSERT(aP1.y != aP2.y,
     737             :              "A horizontal line segment results in dividing by zero error!");
     738             : 
     739           0 :   return aP1.x + (aY - aP1.y) * (aP2.x - aP1.x) / (aP2.y - aP1.y);
     740             : }
     741             : 
     742             : /////////////////////////////////////////////////////////////////////////////
     743             : // FloatInfo
     744             : 
     745           0 : nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame,
     746             :                                      nscoord aLineLeft, nscoord aBlockStart,
     747             :                                      const LogicalRect& aMarginRect,
     748             :                                      WritingMode aWM,
     749           0 :                                      const nsSize& aContainerSize)
     750             :   : mFrame(aFrame)
     751           0 :   , mRect(ShapeInfo::ConvertToFloatLogical(aMarginRect, aWM, aContainerSize) +
     752           0 :           nsPoint(aLineLeft, aBlockStart))
     753             : {
     754           0 :   MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
     755             : 
     756           0 :   const StyleShapeSource& shapeOutside = mFrame->StyleDisplay()->mShapeOutside;
     757             : 
     758           0 :   if (shapeOutside.GetType() == StyleShapeSourceType::None) {
     759           0 :     return;
     760             :   }
     761             : 
     762           0 :   if (shapeOutside.GetType() == StyleShapeSourceType::URL) {
     763             :     // Bug 1265343: Implement 'shape-image-threshold'. Early return
     764             :     // here because shape-outside with url() value doesn't have a
     765             :     // reference box, and GetReferenceBox() asserts that.
     766           0 :     return;
     767             :   }
     768             : 
     769             :   // Initialize <shape-box>'s reference rect.
     770             :   LogicalRect shapeBoxRect =
     771           0 :     ShapeInfo::ComputeShapeBoxRect(shapeOutside, mFrame, aMarginRect, aWM);
     772             : 
     773           0 :   if (shapeOutside.GetType() == StyleShapeSourceType::Box) {
     774           0 :     mShapeInfo = ShapeInfo::CreateShapeBox(mFrame, shapeBoxRect, aWM,
     775           0 :                                            aContainerSize);
     776           0 :   } else if (shapeOutside.GetType() == StyleShapeSourceType::Shape) {
     777           0 :     StyleBasicShape* const basicShape = shapeOutside.GetBasicShape();
     778             : 
     779           0 :     switch (basicShape->GetShapeType()) {
     780             :       case StyleBasicShapeType::Polygon:
     781             :         mShapeInfo =
     782           0 :           ShapeInfo::CreatePolygon(basicShape, shapeBoxRect, aWM,
     783           0 :                                    aContainerSize);
     784           0 :         break;
     785             :       case StyleBasicShapeType::Circle:
     786             :       case StyleBasicShapeType::Ellipse:
     787             :         mShapeInfo =
     788           0 :           ShapeInfo::CreateCircleOrEllipse(basicShape, shapeBoxRect, aWM,
     789           0 :                                            aContainerSize);
     790           0 :         break;
     791             :       case StyleBasicShapeType::Inset:
     792             :         mShapeInfo =
     793           0 :           ShapeInfo::CreateInset(basicShape, shapeBoxRect, aWM, aContainerSize);
     794           0 :         break;
     795             :     }
     796             :   } else {
     797           0 :     MOZ_ASSERT_UNREACHABLE("Unknown StyleShapeSourceType!");
     798             :   }
     799             : 
     800           0 :   MOZ_ASSERT(mShapeInfo,
     801             :              "All shape-outside values except none should have mShapeInfo!");
     802             : 
     803             :   // Translate the shape to the same origin as nsFloatManager.
     804           0 :   mShapeInfo->Translate(aLineLeft, aBlockStart);
     805             : }
     806             : 
     807             : #ifdef NS_BUILD_REFCNT_LOGGING
     808           0 : nsFloatManager::FloatInfo::FloatInfo(FloatInfo&& aOther)
     809           0 :   : mFrame(Move(aOther.mFrame))
     810           0 :   , mLeftBEnd(Move(aOther.mLeftBEnd))
     811           0 :   , mRightBEnd(Move(aOther.mRightBEnd))
     812           0 :   , mRect(Move(aOther.mRect))
     813           0 :   , mShapeInfo(Move(aOther.mShapeInfo))
     814             : {
     815           0 :   MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
     816           0 : }
     817             : 
     818           0 : nsFloatManager::FloatInfo::~FloatInfo()
     819             : {
     820           0 :   MOZ_COUNT_DTOR(nsFloatManager::FloatInfo);
     821           0 : }
     822             : #endif
     823             : 
     824             : nscoord
     825           0 : nsFloatManager::FloatInfo::LineLeft(ShapeType aShapeType,
     826             :                                     const nscoord aBStart,
     827             :                                     const nscoord aBEnd) const
     828             : {
     829           0 :   if (aShapeType == ShapeType::Margin) {
     830           0 :     return LineLeft();
     831             :   }
     832             : 
     833           0 :   MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
     834           0 :   if (!mShapeInfo) {
     835           0 :     return LineLeft();
     836             :   }
     837             :   // Clip the flow area to the margin-box because
     838             :   // https://drafts.csswg.org/css-shapes-1/#relation-to-box-model-and-float-behavior
     839             :   // says "When a shape is used to define a float area, the shape is clipped
     840             :   // to the float’s margin box."
     841           0 :   return std::max(LineLeft(), mShapeInfo->LineLeft(aBStart, aBEnd));
     842             : }
     843             : 
     844             : nscoord
     845           0 : nsFloatManager::FloatInfo::LineRight(ShapeType aShapeType,
     846             :                                      const nscoord aBStart,
     847             :                                      const nscoord aBEnd) const
     848             : {
     849           0 :   if (aShapeType == ShapeType::Margin) {
     850           0 :     return LineRight();
     851             :   }
     852             : 
     853           0 :   MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
     854           0 :   if (!mShapeInfo) {
     855           0 :     return LineRight();
     856             :   }
     857             :   // Clip the flow area to the margin-box. See LineLeft().
     858           0 :   return std::min(LineRight(), mShapeInfo->LineRight(aBStart, aBEnd));
     859             : }
     860             : 
     861             : nscoord
     862           0 : nsFloatManager::FloatInfo::BStart(ShapeType aShapeType) const
     863             : {
     864           0 :   if (aShapeType == ShapeType::Margin) {
     865           0 :     return BStart();
     866             :   }
     867             : 
     868           0 :   MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
     869           0 :   if (!mShapeInfo) {
     870           0 :     return BStart();
     871             :   }
     872             :   // Clip the flow area to the margin-box. See LineLeft().
     873           0 :   return std::max(BStart(), mShapeInfo->BStart());
     874             : }
     875             : 
     876             : nscoord
     877           0 : nsFloatManager::FloatInfo::BEnd(ShapeType aShapeType) const
     878             : {
     879           0 :   if (aShapeType == ShapeType::Margin) {
     880           0 :     return BEnd();
     881             :   }
     882             : 
     883           0 :   MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
     884           0 :   if (!mShapeInfo) {
     885           0 :     return BEnd();
     886             :   }
     887             :   // Clip the flow area to the margin-box. See LineLeft().
     888           0 :   return std::min(BEnd(), mShapeInfo->BEnd());
     889             : }
     890             : 
     891             : bool
     892           0 : nsFloatManager::FloatInfo::IsEmpty(ShapeType aShapeType) const
     893             : {
     894           0 :   if (aShapeType == ShapeType::Margin) {
     895           0 :     return IsEmpty();
     896             :   }
     897             : 
     898           0 :   MOZ_ASSERT(aShapeType == ShapeType::ShapeOutside);
     899           0 :   if (!mShapeInfo) {
     900           0 :     return IsEmpty();
     901             :   }
     902           0 :   return mShapeInfo->IsEmpty();
     903             : }
     904             : 
     905             : /////////////////////////////////////////////////////////////////////////////
     906             : // ShapeInfo
     907             : 
     908             : /* static */ LogicalRect
     909           0 : nsFloatManager::ShapeInfo::ComputeShapeBoxRect(
     910             :   const StyleShapeSource& aShapeOutside,
     911             :   nsIFrame* const aFrame,
     912             :   const mozilla::LogicalRect& aMarginRect,
     913             :   mozilla::WritingMode aWM)
     914             : {
     915           0 :   LogicalRect rect = aMarginRect;
     916             : 
     917           0 :   switch (aShapeOutside.GetReferenceBox()) {
     918             :     case StyleGeometryBox::ContentBox:
     919           0 :       rect.Deflate(aWM, aFrame->GetLogicalUsedPadding(aWM));
     920             :       MOZ_FALLTHROUGH;
     921             :     case StyleGeometryBox::PaddingBox:
     922           0 :       rect.Deflate(aWM, aFrame->GetLogicalUsedBorder(aWM));
     923             :       MOZ_FALLTHROUGH;
     924             :     case StyleGeometryBox::BorderBox:
     925           0 :       rect.Deflate(aWM, aFrame->GetLogicalUsedMargin(aWM));
     926           0 :       break;
     927             :     case StyleGeometryBox::MarginBox:
     928             :       // Do nothing. rect is already a margin rect.
     929           0 :       break;
     930             :     case StyleGeometryBox::NoBox:
     931             :     default:
     932           0 :       MOZ_ASSERT(aShapeOutside.GetType() != StyleShapeSourceType::Box,
     933             :                  "Box source type must have <shape-box> specified!");
     934           0 :       break;
     935             :   }
     936             : 
     937           0 :   return rect;
     938             : }
     939             : 
     940             : /* static */ UniquePtr<nsFloatManager::ShapeInfo>
     941           0 : nsFloatManager::ShapeInfo::CreateShapeBox(
     942             :   nsIFrame* const aFrame,
     943             :   const LogicalRect& aShapeBoxRect,
     944             :   WritingMode aWM,
     945             :   const nsSize& aContainerSize)
     946             : {
     947             :   nsRect logicalShapeBoxRect
     948           0 :     = ConvertToFloatLogical(aShapeBoxRect, aWM, aContainerSize);
     949             : 
     950             :   nscoord physicalRadii[8];
     951           0 :   bool hasRadii = aFrame->GetShapeBoxBorderRadii(physicalRadii);
     952           0 :   if (!hasRadii) {
     953           0 :     return MakeUnique<RoundedBoxShapeInfo>(logicalShapeBoxRect,
     954           0 :                                            UniquePtr<nscoord[]>());
     955             :   }
     956             : 
     957           0 :   return MakeUnique<RoundedBoxShapeInfo>(logicalShapeBoxRect,
     958           0 :                                          ConvertToFloatLogical(physicalRadii,
     959           0 :                                                                aWM));
     960             : }
     961             : 
     962             : /* static */ UniquePtr<nsFloatManager::ShapeInfo>
     963           0 : nsFloatManager::ShapeInfo::CreateInset(
     964             :   const StyleBasicShape* aBasicShape,
     965             :   const LogicalRect& aShapeBoxRect,
     966             :   WritingMode aWM,
     967             :   const nsSize& aContainerSize)
     968             : {
     969             :   // Use physical coordinates to compute inset() because the top, right,
     970             :   // bottom and left offsets are physical.
     971             :   // https://drafts.csswg.org/css-shapes-1/#funcdef-inset
     972             :   nsRect physicalShapeBoxRect =
     973           0 :     aShapeBoxRect.GetPhysicalRect(aWM, aContainerSize);
     974             :   nsRect insetRect =
     975           0 :     ShapeUtils::ComputeInsetRect(aBasicShape, physicalShapeBoxRect);
     976             : 
     977             :   nsRect logicalInsetRect =
     978           0 :     ConvertToFloatLogical(LogicalRect(aWM, insetRect, aContainerSize),
     979           0 :                           aWM, aContainerSize);
     980             :   nscoord physicalRadii[8];
     981             :   bool hasRadii =
     982             :     ShapeUtils::ComputeInsetRadii(aBasicShape, insetRect, physicalShapeBoxRect,
     983           0 :                                   physicalRadii);
     984           0 :   if (!hasRadii) {
     985           0 :     return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
     986           0 :                                            UniquePtr<nscoord[]>());
     987             :   }
     988             : 
     989           0 :   return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
     990           0 :                                          ConvertToFloatLogical(physicalRadii,
     991           0 :                                                                aWM));
     992             : }
     993             : 
     994             : /* static */ UniquePtr<nsFloatManager::ShapeInfo>
     995           0 : nsFloatManager::ShapeInfo::CreateCircleOrEllipse(
     996             :   const StyleBasicShape* aBasicShape,
     997             :   const LogicalRect& aShapeBoxRect,
     998             :   WritingMode aWM,
     999             :   const nsSize& aContainerSize)
    1000             : {
    1001             :   // Use physical coordinates to compute the center of circle() or ellipse()
    1002             :   // since the <position> keywords such as 'left', 'top', etc. are physical.
    1003             :   // https://drafts.csswg.org/css-shapes-1/#funcdef-ellipse
    1004             :   nsRect physicalShapeBoxRect =
    1005           0 :     aShapeBoxRect.GetPhysicalRect(aWM, aContainerSize);
    1006             :   nsPoint physicalCenter =
    1007           0 :     ShapeUtils::ComputeCircleOrEllipseCenter(aBasicShape, physicalShapeBoxRect);
    1008             :   nsPoint logicalCenter =
    1009           0 :     ConvertToFloatLogical(physicalCenter, aWM, aContainerSize);
    1010             : 
    1011             :   // Compute the circle or ellipse radii.
    1012           0 :   nsSize radii;
    1013           0 :   StyleBasicShapeType type = aBasicShape->GetShapeType();
    1014           0 :   if (type == StyleBasicShapeType::Circle) {
    1015             :     nscoord radius = ShapeUtils::ComputeCircleRadius(aBasicShape, physicalCenter,
    1016           0 :                                                      physicalShapeBoxRect);
    1017           0 :     radii = nsSize(radius, radius);
    1018             :   } else {
    1019           0 :     MOZ_ASSERT(type == StyleBasicShapeType::Ellipse);
    1020             :     nsSize physicalRadii =
    1021             :       ShapeUtils::ComputeEllipseRadii(aBasicShape, physicalCenter,
    1022           0 :                                       physicalShapeBoxRect);
    1023           0 :     LogicalSize logicalRadii(aWM, physicalRadii);
    1024           0 :     radii = nsSize(logicalRadii.ISize(aWM), logicalRadii.BSize(aWM));
    1025             :   }
    1026             : 
    1027           0 :   return MakeUnique<EllipseShapeInfo>(logicalCenter, radii);
    1028             : }
    1029             : 
    1030             : /* static */ UniquePtr<nsFloatManager::ShapeInfo>
    1031           0 : nsFloatManager::ShapeInfo::CreatePolygon(
    1032             :   const StyleBasicShape* aBasicShape,
    1033             :   const LogicalRect& aShapeBoxRect,
    1034             :   WritingMode aWM,
    1035             :   const nsSize& aContainerSize)
    1036             : {
    1037             :   // Use physical coordinates to compute each (xi, yi) vertex because CSS
    1038             :   // represents them using physical coordinates.
    1039             :   // https://drafts.csswg.org/css-shapes-1/#funcdef-polygon
    1040             :   nsRect physicalShapeBoxRect =
    1041           0 :     aShapeBoxRect.GetPhysicalRect(aWM, aContainerSize);
    1042             : 
    1043             :   // Get physical vertices.
    1044             :   nsTArray<nsPoint> vertices =
    1045           0 :     ShapeUtils::ComputePolygonVertices(aBasicShape, physicalShapeBoxRect);
    1046             : 
    1047             :   // Convert all the physical vertices to logical.
    1048           0 :   for (nsPoint& vertex : vertices) {
    1049           0 :     vertex = ConvertToFloatLogical(vertex, aWM, aContainerSize);
    1050             :   }
    1051             : 
    1052           0 :   return MakeUnique<PolygonShapeInfo>(Move(vertices));
    1053             : }
    1054             : 
    1055             : /* static */ nscoord
    1056           0 : nsFloatManager::ShapeInfo::ComputeEllipseLineInterceptDiff(
    1057             :   const nscoord aShapeBoxBStart, const nscoord aShapeBoxBEnd,
    1058             :   const nscoord aBStartCornerRadiusL, const nscoord aBStartCornerRadiusB,
    1059             :   const nscoord aBEndCornerRadiusL, const nscoord aBEndCornerRadiusB,
    1060             :   const nscoord aBandBStart, const nscoord aBandBEnd)
    1061             : {
    1062             :   // An example for the band intersecting with the top right corner of an
    1063             :   // ellipse with writing-mode horizontal-tb.
    1064             :   //
    1065             :   //                             lineIntercept lineDiff
    1066             :   //                                    |       |
    1067             :   //  +---------------------------------|-------|-+---- aShapeBoxBStart
    1068             :   //  |                ##########^      |       | |
    1069             :   //  |            ##############|####  |       | |
    1070             :   //  +---------#################|######|-------|-+---- aBandBStart
    1071             :   //  |       ###################|######|##     | |
    1072             :   //  |     aBStartCornerRadiusB |######|###    | |
    1073             :   //  |    ######################|######|#####  | |
    1074             :   //  +---#######################|<-----------><->^---- aBandBEnd
    1075             :   //  |  ########################|##############  |
    1076             :   //  |  ########################|##############  |---- b
    1077             :   //  | #########################|############### |
    1078             :   //  | ######################## v<-------------->v
    1079             :   //  |###################### aBStartCornerRadiusL|
    1080             :   //  |###########################################|
    1081             :   //  |###########################################|
    1082             :   //  |###########################################|
    1083             :   //  |###########################################|
    1084             :   //  | ######################################### |
    1085             :   //  | ######################################### |
    1086             :   //  |  #######################################  |
    1087             :   //  |  #######################################  |
    1088             :   //  |   #####################################   |
    1089             :   //  |    ###################################    |
    1090             :   //  |      ###############################      |
    1091             :   //  |       #############################       |
    1092             :   //  |         #########################         |
    1093             :   //  |            ###################            |
    1094             :   //  |                ###########                |
    1095             :   //  +-------------------------------------------+----- aShapeBoxBEnd
    1096             : 
    1097           0 :   NS_ASSERTION(aShapeBoxBStart <= aShapeBoxBEnd, "Bad shape box coordinates!");
    1098           0 :   NS_ASSERTION(aBandBStart <= aBandBEnd, "Bad band coordinates!");
    1099             : 
    1100           0 :   nscoord lineDiff = 0;
    1101             : 
    1102             :   // If the band intersects both the block-start and block-end corners, we
    1103             :   // don't need to enter either branch because the correct lineDiff is 0.
    1104           0 :   if (aBStartCornerRadiusB > 0 &&
    1105           0 :       aBandBEnd >= aShapeBoxBStart &&
    1106           0 :       aBandBEnd <= aShapeBoxBStart + aBStartCornerRadiusB) {
    1107             :     // The band intersects only the block-start corner.
    1108           0 :     nscoord b = aBStartCornerRadiusB - (aBandBEnd - aShapeBoxBStart);
    1109             :     nscoord lineIntercept =
    1110           0 :       XInterceptAtY(b, aBStartCornerRadiusL, aBStartCornerRadiusB);
    1111           0 :     lineDiff = aBStartCornerRadiusL - lineIntercept;
    1112           0 :   } else if (aBEndCornerRadiusB > 0 &&
    1113           0 :              aBandBStart >= aShapeBoxBEnd - aBEndCornerRadiusB &&
    1114             :              aBandBStart <= aShapeBoxBEnd) {
    1115             :     // The band intersects only the block-end corner.
    1116           0 :     nscoord b = aBEndCornerRadiusB - (aShapeBoxBEnd - aBandBStart);
    1117             :     nscoord lineIntercept =
    1118           0 :       XInterceptAtY(b, aBEndCornerRadiusL, aBEndCornerRadiusB);
    1119           0 :     lineDiff = aBEndCornerRadiusL - lineIntercept;
    1120             :   }
    1121             : 
    1122           0 :   return lineDiff;
    1123             : }
    1124             : 
    1125             : /* static */ nscoord
    1126           0 : nsFloatManager::ShapeInfo::XInterceptAtY(const nscoord aY,
    1127             :                                          const nscoord aRadiusX,
    1128             :                                          const nscoord aRadiusY)
    1129             : {
    1130             :   // Solve for x in the ellipse equation (x/radiusX)^2 + (y/radiusY)^2 = 1.
    1131           0 :   MOZ_ASSERT(aRadiusY > 0);
    1132           0 :   return aRadiusX * std::sqrt(1 - (aY * aY) / double(aRadiusY * aRadiusY));
    1133             : }
    1134             : 
    1135             : /* static */ nsPoint
    1136           0 : nsFloatManager::ShapeInfo::ConvertToFloatLogical(
    1137             :   const nsPoint& aPoint,
    1138             :   WritingMode aWM,
    1139             :   const nsSize& aContainerSize)
    1140             : {
    1141           0 :   LogicalPoint logicalPoint(aWM, aPoint, aContainerSize);
    1142             :   return nsPoint(logicalPoint.LineRelative(aWM, aContainerSize),
    1143           0 :                  logicalPoint.B(aWM));
    1144             : }
    1145             : 
    1146             : /* static */ UniquePtr<nscoord[]>
    1147           0 : nsFloatManager::ShapeInfo::ConvertToFloatLogical(const nscoord aRadii[8],
    1148             :                                                  WritingMode aWM)
    1149             : {
    1150           0 :   UniquePtr<nscoord[]> logicalRadii(new nscoord[8]);
    1151             : 
    1152             :   // Get the physical side for line-left and line-right since border radii
    1153             :   // are on the physical axis.
    1154             :   Side lineLeftSide =
    1155           0 :     aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirLeft));
    1156           0 :   logicalRadii[eCornerTopLeftX] =
    1157           0 :     aRadii[SideToHalfCorner(lineLeftSide, true, false)];
    1158           0 :   logicalRadii[eCornerTopLeftY] =
    1159           0 :     aRadii[SideToHalfCorner(lineLeftSide, true, true)];
    1160           0 :   logicalRadii[eCornerBottomLeftX] =
    1161           0 :     aRadii[SideToHalfCorner(lineLeftSide, false, false)];
    1162           0 :   logicalRadii[eCornerBottomLeftY] =
    1163           0 :     aRadii[SideToHalfCorner(lineLeftSide, false, true)];
    1164             : 
    1165             :   Side lineRightSide =
    1166           0 :     aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirRight));
    1167           0 :   logicalRadii[eCornerTopRightX] =
    1168           0 :     aRadii[SideToHalfCorner(lineRightSide, false, false)];
    1169           0 :   logicalRadii[eCornerTopRightY] =
    1170           0 :     aRadii[SideToHalfCorner(lineRightSide, false, true)];
    1171           0 :   logicalRadii[eCornerBottomRightX] =
    1172           0 :     aRadii[SideToHalfCorner(lineRightSide, true, false)];
    1173           0 :   logicalRadii[eCornerBottomRightY] =
    1174           0 :     aRadii[SideToHalfCorner(lineRightSide, true, true)];
    1175             : 
    1176           0 :   if (aWM.IsLineInverted()) {
    1177             :     // When IsLineInverted() is true, i.e. aWM is vertical-lr,
    1178             :     // line-over/line-under are inverted from block-start/block-end. So the
    1179             :     // relationship reverses between which corner comes first going
    1180             :     // clockwise, and which corner is block-start versus block-end. We need
    1181             :     // to swap the values stored in top and bottom corners.
    1182           0 :     std::swap(logicalRadii[eCornerTopLeftX], logicalRadii[eCornerBottomLeftX]);
    1183           0 :     std::swap(logicalRadii[eCornerTopLeftY], logicalRadii[eCornerBottomLeftY]);
    1184           0 :     std::swap(logicalRadii[eCornerTopRightX], logicalRadii[eCornerBottomRightX]);
    1185           0 :     std::swap(logicalRadii[eCornerTopRightY], logicalRadii[eCornerBottomRightY]);
    1186             :   }
    1187             : 
    1188           0 :   return logicalRadii;
    1189             : }
    1190             : 
    1191             : //----------------------------------------------------------------------
    1192             : 
    1193         324 : nsAutoFloatManager::~nsAutoFloatManager()
    1194             : {
    1195             :   // Restore the old float manager in the reflow input if necessary.
    1196         162 :   if (mNew) {
    1197             : #ifdef DEBUG
    1198         152 :     if (nsBlockFrame::gNoisyFloatManager) {
    1199           0 :       printf("restoring old float manager %p\n", mOld);
    1200             :     }
    1201             : #endif
    1202             : 
    1203         152 :     mReflowInput.mFloatManager = mOld;
    1204             : 
    1205             : #ifdef DEBUG
    1206         152 :     if (nsBlockFrame::gNoisyFloatManager) {
    1207           0 :       if (mOld) {
    1208           0 :         mReflowInput.mFrame->ListTag(stdout);
    1209           0 :         printf(": float manager %p after reflow\n", mOld);
    1210           0 :         mOld->List(stdout);
    1211             :       }
    1212             :     }
    1213             : #endif
    1214             :   }
    1215         162 : }
    1216             : 
    1217             : void
    1218         152 : nsAutoFloatManager::CreateFloatManager(nsPresContext *aPresContext)
    1219             : {
    1220         152 :   MOZ_ASSERT(!mNew, "Redundant call to CreateFloatManager!");
    1221             : 
    1222             :   // Create a new float manager and install it in the reflow
    1223             :   // input. `Remember' the old float manager so we can restore it
    1224             :   // later.
    1225         304 :   mNew = MakeUnique<nsFloatManager>(aPresContext->PresShell(),
    1226         456 :                                     mReflowInput.GetWritingMode());
    1227             : 
    1228             : #ifdef DEBUG
    1229         152 :   if (nsBlockFrame::gNoisyFloatManager) {
    1230           0 :     printf("constructed new float manager %p (replacing %p)\n",
    1231           0 :            mNew.get(), mReflowInput.mFloatManager);
    1232             :   }
    1233             : #endif
    1234             : 
    1235             :   // Set the float manager in the existing reflow input.
    1236         152 :   mOld = mReflowInput.mFloatManager;
    1237         152 :   mReflowInput.mFloatManager = mNew.get();
    1238         152 : }

Generated by: LCOV version 1.13